staging/bluetooth: Add hci_h4p driver
Add hci_h4p bluetooth driver to staging tree. This device is used for example on Nokia N900 cell phone. Signed-off-by: Pali Rohár <pali.rohar@gmail.com> Signed-off-by: Pavel Machek <pavel@ucw.cz> Thanks-to: Sebastian Reichel <sre@debian.org> Thanks-to: Joe Perches <joe@perches.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
be973fcd81
commit
91eef3e2fe
13 changed files with 2430 additions and 0 deletions
|
|
@ -148,4 +148,6 @@ source "drivers/staging/dgap/Kconfig"
|
||||||
|
|
||||||
source "drivers/staging/gs_fpgaboot/Kconfig"
|
source "drivers/staging/gs_fpgaboot/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/staging/nokia_h4p/Kconfig"
|
||||||
|
|
||||||
endif # STAGING
|
endif # STAGING
|
||||||
|
|
|
||||||
|
|
@ -66,3 +66,4 @@ obj-$(CONFIG_DGNC) += dgnc/
|
||||||
obj-$(CONFIG_DGAP) += dgap/
|
obj-$(CONFIG_DGAP) += dgap/
|
||||||
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
|
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
|
||||||
obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/
|
obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/
|
||||||
|
obj-$(CONFIG_BT_NOKIA_H4P) += nokia_h4p/
|
||||||
|
|
|
||||||
9
drivers/staging/nokia_h4p/Kconfig
Normal file
9
drivers/staging/nokia_h4p/Kconfig
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
config BT_NOKIA_H4P
|
||||||
|
tristate "HCI driver with H4 Nokia extensions"
|
||||||
|
depends on BT && ARCH_OMAP
|
||||||
|
help
|
||||||
|
Bluetooth HCI driver with H4 extensions. This driver provides
|
||||||
|
support for H4+ Bluetooth chip with vendor-specific H4 extensions.
|
||||||
|
|
||||||
|
Say Y here to compile support for h4 extended devices into the kernel
|
||||||
|
or say M to compile it as module (btnokia_h4p).
|
||||||
6
drivers/staging/nokia_h4p/Makefile
Normal file
6
drivers/staging/nokia_h4p/Makefile
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
obj-$(CONFIG_BT_NOKIA_H4P) += btnokia_h4p.o
|
||||||
|
btnokia_h4p-objs := nokia_core.o nokia_fw.o nokia_uart.o nokia_fw-csr.o \
|
||||||
|
nokia_fw-bcm.o nokia_fw-ti1273.o
|
||||||
|
|
||||||
|
ccflags-y += -D__CHECK_ENDIAN__
|
||||||
140
drivers/staging/nokia_h4p/TODO
Normal file
140
drivers/staging/nokia_h4p/TODO
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
Few attempts to submission have been made, last review comments were received in
|
||||||
|
|
||||||
|
Date: Wed, 15 Jan 2014 19:01:51 -0800
|
||||||
|
From: Marcel Holtmann <marcel@holtmann.org>
|
||||||
|
Subject: Re: [PATCH v6] Bluetooth: Add hci_h4p driver
|
||||||
|
|
||||||
|
Some code refactoring is still needed.
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
|
||||||
|
> +++ b/drivers/bluetooth/hci_h4p.h
|
||||||
|
|
||||||
|
can we please get the naming straight. File names do not start with
|
||||||
|
hci_ anymore. We moved away from it since that term is too generic.
|
||||||
|
|
||||||
|
> +#define FW_NAME_TI1271_LE "ti1273_le.bin"
|
||||||
|
> +#define FW_NAME_TI1271 "ti1273.bin"
|
||||||
|
> +#define FW_NAME_BCM2048 "bcmfw.bin"
|
||||||
|
> +#define FW_NAME_CSR "bc4fw.bin"
|
||||||
|
|
||||||
|
We do these have to be global in a header file. This should be
|
||||||
|
confined to the specific firmware part.
|
||||||
|
|
||||||
|
> +struct hci_h4p_info {
|
||||||
|
|
||||||
|
Can we please get rid of the hci_ prefix for everything. Copying from
|
||||||
|
drivers that are over 10 years old is not a good idea. Please look at
|
||||||
|
recent ones.
|
||||||
|
|
||||||
|
> + struct timer_list lazy_release;
|
||||||
|
|
||||||
|
Timer? Not delayed work?
|
||||||
|
|
||||||
|
> +void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val);
|
||||||
|
> +u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset);
|
||||||
|
> +void hci_h4p_set_rts(struct hci_h4p_info *info, int active);
|
||||||
|
> +int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active, int timeout_ms);
|
||||||
|
> +void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
|
||||||
|
> +void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
|
||||||
|
> +void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed);
|
||||||
|
> +int hci_h4p_reset_uart(struct hci_h4p_info *info);
|
||||||
|
> +void hci_h4p_init_uart(struct hci_h4p_info *info);
|
||||||
|
> +void hci_h4p_enable_tx(struct hci_h4p_info *info);
|
||||||
|
> +void hci_h4p_store_regs(struct hci_h4p_info *info);
|
||||||
|
> +void hci_h4p_restore_regs(struct hci_h4p_info *info);
|
||||||
|
> +void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable);
|
||||||
|
|
||||||
|
These are a lot of public functions. Are they all really needed or can
|
||||||
|
the code be done smart.
|
||||||
|
|
||||||
|
> +static ssize_t hci_h4p_store_bdaddr(struct device *dev,
|
||||||
|
> + struct device_attribute *attr,
|
||||||
|
> + const char *buf, size_t count)
|
||||||
|
> +{
|
||||||
|
> + struct hci_h4p_info *info = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
Since none of these devices can function without having a valid
|
||||||
|
address, the way this should work is that we should not register the
|
||||||
|
HCI device when probing the platform device.
|
||||||
|
|
||||||
|
The HCI device should be registered once a valid address has been
|
||||||
|
written into the sysfs file. I do not want to play the tricks with
|
||||||
|
bringing up the device without a valid address.
|
||||||
|
|
||||||
|
> + hdev->close = hci_h4p_hci_close;
|
||||||
|
> + hdev->flush = hci_h4p_hci_flush;
|
||||||
|
> + hdev->send = hci_h4p_hci_send_frame;
|
||||||
|
|
||||||
|
It needs to use hdev->setup to load the firmware. I assume the
|
||||||
|
firmware only needs to be loaded once. That is exactly what
|
||||||
|
hdev->setup does. It gets executed once.
|
||||||
|
|
||||||
|
> + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||||
|
|
||||||
|
Is this quirk really needed? Normally only Bluetooth 1.1 and early
|
||||||
|
devices qualify for it.
|
||||||
|
|
||||||
|
> +static int hci_h4p_bcm_set_bdaddr(struct hci_h4p_info *info, struct sk_buff *skb)
|
||||||
|
> +{
|
||||||
|
> + int i;
|
||||||
|
> + static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
|
||||||
|
> + int not_valid;
|
||||||
|
|
||||||
|
Has this actually been confirmed that we can just randomly set an
|
||||||
|
address out of the Nokia range. I do not think so. This is a pretty
|
||||||
|
bad idea.
|
||||||
|
|
||||||
|
I have no interest in merging a driver with such a hack.
|
||||||
|
|
||||||
|
> + not_valid = 1;
|
||||||
|
> + for (i = 0; i < 6; i++) {
|
||||||
|
> + if (info->bd_addr[i] != 0x00) {
|
||||||
|
> + not_valid = 0;
|
||||||
|
> + break;
|
||||||
|
> + }
|
||||||
|
> + }
|
||||||
|
|
||||||
|
Anybody every heard of memcmp or bacmp and BDADDR_ANY?
|
||||||
|
|
||||||
|
> + if (not_valid) {
|
||||||
|
> + dev_info(info->dev, "Valid bluetooth address not found,"
|
||||||
|
> + " setting some random\n");
|
||||||
|
> + /* When address is not valid, use some random */
|
||||||
|
> + memcpy(info->bd_addr, nokia_oui, 3);
|
||||||
|
> + get_random_bytes(info->bd_addr + 3, 3);
|
||||||
|
> + }
|
||||||
|
|
||||||
|
|
||||||
|
And why does every single chip firmware does this differently. Seriously, this is a mess.
|
||||||
|
|
||||||
|
> +void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
|
||||||
|
> +{
|
||||||
|
> + switch (info->man_id) {
|
||||||
|
> + case H4P_ID_CSR:
|
||||||
|
> + hci_h4p_bc4_parse_fw_event(info, skb);
|
||||||
|
> + break;
|
||||||
|
...
|
||||||
|
> +}
|
||||||
|
|
||||||
|
We have proper HCI sync command handling in recent kernels. I really
|
||||||
|
do not know why this is hand coded these days. Check how the Intel
|
||||||
|
firmware loading inside btusb.c does it.
|
||||||
|
|
||||||
|
> +inline u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset)
|
||||||
|
> +{
|
||||||
|
> + return __raw_readb(info->uart_base + (offset << 2));
|
||||||
|
> +}
|
||||||
|
|
||||||
|
Inline in a *.c file for a non-static function. Makes no sense to me.
|
||||||
|
|
||||||
|
> +/**
|
||||||
|
> + * struct hci_h4p_platform data - hci_h4p Platform data structure
|
||||||
|
> + */
|
||||||
|
> +struct hci_h4p_platform_data {
|
||||||
|
|
||||||
|
please have a proper name here. For example
|
||||||
|
btnokia_h4p_platform_data.
|
||||||
|
|
||||||
|
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
|
||||||
|
Pavel Machek <pavel@ucw.cz>
|
||||||
228
drivers/staging/nokia_h4p/hci_h4p.h
Normal file
228
drivers/staging/nokia_h4p/hci_h4p.h
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Nokia H4P bluetooth driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-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 __DRIVERS_BLUETOOTH_HCI_H4P_H
|
||||||
|
#define __DRIVERS_BLUETOOTH_HCI_H4P_H
|
||||||
|
|
||||||
|
#include <net/bluetooth/bluetooth.h>
|
||||||
|
#include <net/bluetooth/hci_core.h>
|
||||||
|
#include <net/bluetooth/hci.h>
|
||||||
|
|
||||||
|
#define FW_NAME_TI1271_PRELE "ti1273_prele.bin"
|
||||||
|
#define FW_NAME_TI1271_LE "ti1273_le.bin"
|
||||||
|
#define FW_NAME_TI1271 "ti1273.bin"
|
||||||
|
#define FW_NAME_BCM2048 "bcmfw.bin"
|
||||||
|
#define FW_NAME_CSR "bc4fw.bin"
|
||||||
|
|
||||||
|
#define UART_SYSC_OMAP_RESET 0x03
|
||||||
|
#define UART_SYSS_RESETDONE 0x01
|
||||||
|
#define UART_OMAP_SCR_EMPTY_THR 0x08
|
||||||
|
#define UART_OMAP_SCR_WAKEUP 0x10
|
||||||
|
#define UART_OMAP_SSR_WAKEUP 0x02
|
||||||
|
#define UART_OMAP_SSR_TXFULL 0x01
|
||||||
|
|
||||||
|
#define UART_OMAP_SYSC_IDLEMODE 0x03
|
||||||
|
#define UART_OMAP_SYSC_IDLEMASK (3 << UART_OMAP_SYSC_IDLEMODE)
|
||||||
|
|
||||||
|
#define UART_OMAP_SYSC_FORCE_IDLE (0 << UART_OMAP_SYSC_IDLEMODE)
|
||||||
|
#define UART_OMAP_SYSC_NO_IDLE (1 << UART_OMAP_SYSC_IDLEMODE)
|
||||||
|
#define UART_OMAP_SYSC_SMART_IDLE (2 << UART_OMAP_SYSC_IDLEMODE)
|
||||||
|
|
||||||
|
#define H4P_TRANSFER_MODE 1
|
||||||
|
#define H4P_SCHED_TRANSFER_MODE 2
|
||||||
|
#define H4P_ACTIVE_MODE 3
|
||||||
|
|
||||||
|
struct hci_h4p_info {
|
||||||
|
struct timer_list lazy_release;
|
||||||
|
struct hci_dev *hdev;
|
||||||
|
spinlock_t lock;
|
||||||
|
|
||||||
|
void __iomem *uart_base;
|
||||||
|
unsigned long uart_phys_base;
|
||||||
|
int irq;
|
||||||
|
struct device *dev;
|
||||||
|
u8 chip_type;
|
||||||
|
u8 bt_wakeup_gpio;
|
||||||
|
u8 host_wakeup_gpio;
|
||||||
|
u8 reset_gpio;
|
||||||
|
u8 reset_gpio_shared;
|
||||||
|
u8 bt_sysclk;
|
||||||
|
u8 man_id;
|
||||||
|
u8 ver_id;
|
||||||
|
|
||||||
|
struct sk_buff_head fw_queue;
|
||||||
|
struct sk_buff *alive_cmd_skb;
|
||||||
|
struct completion init_completion;
|
||||||
|
struct completion fw_completion;
|
||||||
|
struct completion test_completion;
|
||||||
|
int fw_error;
|
||||||
|
int init_error;
|
||||||
|
|
||||||
|
struct sk_buff_head txq;
|
||||||
|
|
||||||
|
struct sk_buff *rx_skb;
|
||||||
|
long rx_count;
|
||||||
|
unsigned long rx_state;
|
||||||
|
unsigned long garbage_bytes;
|
||||||
|
|
||||||
|
u8 bd_addr[6];
|
||||||
|
struct sk_buff_head *fw_q;
|
||||||
|
|
||||||
|
int pm_enabled;
|
||||||
|
int tx_enabled;
|
||||||
|
int autorts;
|
||||||
|
int rx_enabled;
|
||||||
|
unsigned long pm_flags;
|
||||||
|
|
||||||
|
int tx_clocks_en;
|
||||||
|
int rx_clocks_en;
|
||||||
|
spinlock_t clocks_lock;
|
||||||
|
struct clk *uart_iclk;
|
||||||
|
struct clk *uart_fclk;
|
||||||
|
atomic_t clk_users;
|
||||||
|
u16 dll;
|
||||||
|
u16 dlh;
|
||||||
|
u16 ier;
|
||||||
|
u16 mdr1;
|
||||||
|
u16 efr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hci_h4p_radio_hdr {
|
||||||
|
__u8 evt;
|
||||||
|
__u8 dlen;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct hci_h4p_neg_hdr {
|
||||||
|
__u8 dlen;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
#define H4P_NEG_HDR_SIZE 1
|
||||||
|
|
||||||
|
#define H4P_NEG_REQ 0x00
|
||||||
|
#define H4P_NEG_ACK 0x20
|
||||||
|
#define H4P_NEG_NAK 0x40
|
||||||
|
|
||||||
|
#define H4P_PROTO_PKT 0x44
|
||||||
|
#define H4P_PROTO_BYTE 0x4c
|
||||||
|
|
||||||
|
#define H4P_ID_CSR 0x02
|
||||||
|
#define H4P_ID_BCM2048 0x04
|
||||||
|
#define H4P_ID_TI1271 0x31
|
||||||
|
|
||||||
|
struct hci_h4p_neg_cmd {
|
||||||
|
__u8 ack;
|
||||||
|
__u16 baud;
|
||||||
|
__u16 unused1;
|
||||||
|
__u8 proto;
|
||||||
|
__u16 sys_clk;
|
||||||
|
__u16 unused2;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
struct hci_h4p_neg_evt {
|
||||||
|
__u8 ack;
|
||||||
|
__u16 baud;
|
||||||
|
__u16 unused1;
|
||||||
|
__u8 proto;
|
||||||
|
__u16 sys_clk;
|
||||||
|
__u16 unused2;
|
||||||
|
__u8 man_id;
|
||||||
|
__u8 ver_id;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
#define H4P_ALIVE_REQ 0x55
|
||||||
|
#define H4P_ALIVE_RESP 0xcc
|
||||||
|
|
||||||
|
struct hci_h4p_alive_hdr {
|
||||||
|
__u8 dlen;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
#define H4P_ALIVE_HDR_SIZE 1
|
||||||
|
|
||||||
|
struct hci_h4p_alive_pkt {
|
||||||
|
__u8 mid;
|
||||||
|
__u8 unused;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
#define MAX_BAUD_RATE 921600
|
||||||
|
#define BC4_MAX_BAUD_RATE 3692300
|
||||||
|
#define UART_CLOCK 48000000
|
||||||
|
#define BT_INIT_DIVIDER 320
|
||||||
|
#define BT_BAUDRATE_DIVIDER 384000000
|
||||||
|
#define BT_SYSCLK_DIV 1000
|
||||||
|
#define INIT_SPEED 120000
|
||||||
|
|
||||||
|
#define H4_TYPE_SIZE 1
|
||||||
|
#define H4_RADIO_HDR_SIZE 2
|
||||||
|
|
||||||
|
/* H4+ packet types */
|
||||||
|
#define H4_CMD_PKT 0x01
|
||||||
|
#define H4_ACL_PKT 0x02
|
||||||
|
#define H4_SCO_PKT 0x03
|
||||||
|
#define H4_EVT_PKT 0x04
|
||||||
|
#define H4_NEG_PKT 0x06
|
||||||
|
#define H4_ALIVE_PKT 0x07
|
||||||
|
#define H4_RADIO_PKT 0x08
|
||||||
|
|
||||||
|
/* TX states */
|
||||||
|
#define WAIT_FOR_PKT_TYPE 1
|
||||||
|
#define WAIT_FOR_HEADER 2
|
||||||
|
#define WAIT_FOR_DATA 3
|
||||||
|
|
||||||
|
struct hci_fw_event {
|
||||||
|
struct hci_event_hdr hev;
|
||||||
|
struct hci_ev_cmd_complete cmd;
|
||||||
|
u8 status;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
int hci_h4p_send_alive_packet(struct hci_h4p_info *info);
|
||||||
|
|
||||||
|
void hci_h4p_bcm_parse_fw_event(struct hci_h4p_info *info,
|
||||||
|
struct sk_buff *skb);
|
||||||
|
int hci_h4p_bcm_send_fw(struct hci_h4p_info *info,
|
||||||
|
struct sk_buff_head *fw_queue);
|
||||||
|
|
||||||
|
void hci_h4p_bc4_parse_fw_event(struct hci_h4p_info *info,
|
||||||
|
struct sk_buff *skb);
|
||||||
|
int hci_h4p_bc4_send_fw(struct hci_h4p_info *info,
|
||||||
|
struct sk_buff_head *fw_queue);
|
||||||
|
|
||||||
|
void hci_h4p_ti1273_parse_fw_event(struct hci_h4p_info *info,
|
||||||
|
struct sk_buff *skb);
|
||||||
|
int hci_h4p_ti1273_send_fw(struct hci_h4p_info *info,
|
||||||
|
struct sk_buff_head *fw_queue);
|
||||||
|
|
||||||
|
int hci_h4p_read_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue);
|
||||||
|
int hci_h4p_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue);
|
||||||
|
void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb);
|
||||||
|
|
||||||
|
void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val);
|
||||||
|
u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset);
|
||||||
|
void hci_h4p_set_rts(struct hci_h4p_info *info, int active);
|
||||||
|
int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active, int timeout_ms);
|
||||||
|
void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
|
||||||
|
void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
|
||||||
|
void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed);
|
||||||
|
int hci_h4p_reset_uart(struct hci_h4p_info *info);
|
||||||
|
void hci_h4p_init_uart(struct hci_h4p_info *info);
|
||||||
|
void hci_h4p_enable_tx(struct hci_h4p_info *info);
|
||||||
|
void hci_h4p_store_regs(struct hci_h4p_info *info);
|
||||||
|
void hci_h4p_restore_regs(struct hci_h4p_info *info);
|
||||||
|
void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable);
|
||||||
|
|
||||||
|
#endif /* __DRIVERS_BLUETOOTH_HCI_H4P_H */
|
||||||
1205
drivers/staging/nokia_h4p/nokia_core.c
Normal file
1205
drivers/staging/nokia_h4p/nokia_core.c
Normal file
File diff suppressed because it is too large
Load diff
147
drivers/staging/nokia_h4p/nokia_fw-bcm.c
Normal file
147
drivers/staging/nokia_h4p/nokia_fw-bcm.c
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Nokia H4P bluetooth driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-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 <linux/skbuff.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/serial_reg.h>
|
||||||
|
|
||||||
|
#include "hci_h4p.h"
|
||||||
|
|
||||||
|
static int hci_h4p_bcm_set_bdaddr(struct hci_h4p_info *info, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
|
||||||
|
int not_valid;
|
||||||
|
|
||||||
|
not_valid = 1;
|
||||||
|
for (i = 0; i < 6; i++) {
|
||||||
|
if (info->bd_addr[i] != 0x00) {
|
||||||
|
not_valid = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not_valid) {
|
||||||
|
dev_info(info->dev, "Valid bluetooth address not found, setting some random\n");
|
||||||
|
/* When address is not valid, use some random but Nokia MAC */
|
||||||
|
memcpy(info->bd_addr, nokia_oui, 3);
|
||||||
|
get_random_bytes(info->bd_addr + 3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 6; i++)
|
||||||
|
skb->data[9 - i] = info->bd_addr[i];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_h4p_bcm_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct sk_buff *fw_skb;
|
||||||
|
int err;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (skb->data[5] != 0x00) {
|
||||||
|
dev_err(info->dev, "Firmware sending command failed 0x%.2x\n",
|
||||||
|
skb->data[5]);
|
||||||
|
info->fw_error = -EPROTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
fw_skb = skb_dequeue(info->fw_q);
|
||||||
|
if (fw_skb == NULL || info->fw_error) {
|
||||||
|
complete(&info->fw_completion);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fw_skb->data[1] == 0x01 && fw_skb->data[2] == 0xfc && fw_skb->len >= 10) {
|
||||||
|
BT_DBG("Setting bluetooth address");
|
||||||
|
err = hci_h4p_bcm_set_bdaddr(info, fw_skb);
|
||||||
|
if (err < 0) {
|
||||||
|
kfree_skb(fw_skb);
|
||||||
|
info->fw_error = err;
|
||||||
|
complete(&info->fw_completion);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skb_queue_tail(&info->txq, fw_skb);
|
||||||
|
spin_lock_irqsave(&info->lock, flags);
|
||||||
|
hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
|
||||||
|
UART_IER_THRI);
|
||||||
|
spin_unlock_irqrestore(&info->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hci_h4p_bcm_send_fw(struct hci_h4p_info *info,
|
||||||
|
struct sk_buff_head *fw_queue)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
unsigned long flags, time;
|
||||||
|
|
||||||
|
info->fw_error = 0;
|
||||||
|
|
||||||
|
BT_DBG("Sending firmware");
|
||||||
|
|
||||||
|
time = jiffies;
|
||||||
|
|
||||||
|
info->fw_q = fw_queue;
|
||||||
|
skb = skb_dequeue(fw_queue);
|
||||||
|
if (!skb)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
BT_DBG("Sending commands");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable smart-idle as UART TX interrupts
|
||||||
|
* are not wake-up capable
|
||||||
|
*/
|
||||||
|
hci_h4p_smart_idle(info, 0);
|
||||||
|
|
||||||
|
/* Check if this is bd_address packet */
|
||||||
|
init_completion(&info->fw_completion);
|
||||||
|
skb_queue_tail(&info->txq, skb);
|
||||||
|
spin_lock_irqsave(&info->lock, flags);
|
||||||
|
hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
|
||||||
|
UART_IER_THRI);
|
||||||
|
spin_unlock_irqrestore(&info->lock, flags);
|
||||||
|
|
||||||
|
if (!wait_for_completion_timeout(&info->fw_completion,
|
||||||
|
msecs_to_jiffies(2000))) {
|
||||||
|
dev_err(info->dev, "No reply to fw command\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->fw_error) {
|
||||||
|
dev_err(info->dev, "FW error\n");
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_DBG("Firmware sent in %d msecs",
|
||||||
|
jiffies_to_msecs(jiffies-time));
|
||||||
|
|
||||||
|
hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
|
||||||
|
hci_h4p_set_rts(info, 0);
|
||||||
|
hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
|
||||||
|
hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
150
drivers/staging/nokia_h4p/nokia_fw-csr.c
Normal file
150
drivers/staging/nokia_h4p/nokia_fw-csr.c
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Nokia H4P bluetooth driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005-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 <linux/skbuff.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/serial_reg.h>
|
||||||
|
|
||||||
|
#include "hci_h4p.h"
|
||||||
|
|
||||||
|
void hci_h4p_bc4_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
/* Check if this is fw packet */
|
||||||
|
if (skb->data[0] != 0xff) {
|
||||||
|
hci_recv_frame(info->hdev, skb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skb->data[11] || skb->data[12]) {
|
||||||
|
dev_err(info->dev, "Firmware sending command failed\n");
|
||||||
|
info->fw_error = -EPROTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree_skb(skb);
|
||||||
|
complete(&info->fw_completion);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hci_h4p_bc4_send_fw(struct hci_h4p_info *info,
|
||||||
|
struct sk_buff_head *fw_queue)
|
||||||
|
{
|
||||||
|
static const u8 nokia_oui[3] = {0x00, 0x19, 0x4F};
|
||||||
|
struct sk_buff *skb;
|
||||||
|
unsigned int offset;
|
||||||
|
int retries, count, i, not_valid;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
info->fw_error = 0;
|
||||||
|
|
||||||
|
BT_DBG("Sending firmware");
|
||||||
|
skb = skb_dequeue(fw_queue);
|
||||||
|
|
||||||
|
if (!skb)
|
||||||
|
return -ENOMSG;
|
||||||
|
|
||||||
|
/* Check if this is bd_address packet */
|
||||||
|
if (skb->data[15] == 0x01 && skb->data[16] == 0x00) {
|
||||||
|
offset = 21;
|
||||||
|
skb->data[offset + 1] = 0x00;
|
||||||
|
skb->data[offset + 5] = 0x00;
|
||||||
|
|
||||||
|
not_valid = 1;
|
||||||
|
for (i = 0; i < 6; i++) {
|
||||||
|
if (info->bd_addr[i] != 0x00) {
|
||||||
|
not_valid = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not_valid) {
|
||||||
|
dev_info(info->dev, "Valid bluetooth address not found,"
|
||||||
|
" setting some random\n");
|
||||||
|
/* When address is not valid, use some random */
|
||||||
|
memcpy(info->bd_addr, nokia_oui, 3);
|
||||||
|
get_random_bytes(info->bd_addr + 3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
skb->data[offset + 7] = info->bd_addr[0];
|
||||||
|
skb->data[offset + 6] = info->bd_addr[1];
|
||||||
|
skb->data[offset + 4] = info->bd_addr[2];
|
||||||
|
skb->data[offset + 0] = info->bd_addr[3];
|
||||||
|
skb->data[offset + 3] = info->bd_addr[4];
|
||||||
|
skb->data[offset + 2] = info->bd_addr[5];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (count = 1; ; count++) {
|
||||||
|
BT_DBG("Sending firmware command %d", count);
|
||||||
|
init_completion(&info->fw_completion);
|
||||||
|
skb_queue_tail(&info->txq, skb);
|
||||||
|
spin_lock_irqsave(&info->lock, flags);
|
||||||
|
hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
|
||||||
|
UART_IER_THRI);
|
||||||
|
spin_unlock_irqrestore(&info->lock, flags);
|
||||||
|
|
||||||
|
skb = skb_dequeue(fw_queue);
|
||||||
|
if (!skb)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!wait_for_completion_timeout(&info->fw_completion,
|
||||||
|
msecs_to_jiffies(1000))) {
|
||||||
|
dev_err(info->dev, "No reply to fw command\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->fw_error) {
|
||||||
|
dev_err(info->dev, "FW error\n");
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Wait for chip warm reset */
|
||||||
|
retries = 100;
|
||||||
|
while ((!skb_queue_empty(&info->txq) ||
|
||||||
|
!(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT)) &&
|
||||||
|
retries--) {
|
||||||
|
msleep(10);
|
||||||
|
}
|
||||||
|
if (!retries) {
|
||||||
|
dev_err(info->dev, "Transmitter not empty\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
|
||||||
|
|
||||||
|
if (hci_h4p_wait_for_cts(info, 1, 100)) {
|
||||||
|
dev_err(info->dev, "cts didn't deassert after final speed\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
retries = 100;
|
||||||
|
do {
|
||||||
|
init_completion(&info->init_completion);
|
||||||
|
hci_h4p_send_alive_packet(info);
|
||||||
|
retries--;
|
||||||
|
} while (!wait_for_completion_timeout(&info->init_completion, 100) &&
|
||||||
|
retries > 0);
|
||||||
|
|
||||||
|
if (!retries) {
|
||||||
|
dev_err(info->dev, "No alive reply after speed change\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
110
drivers/staging/nokia_h4p/nokia_fw-ti1273.c
Normal file
110
drivers/staging/nokia_h4p/nokia_fw-ti1273.c
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Nokia H4P bluetooth driver
|
||||||
|
*
|
||||||
|
* 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 <linux/skbuff.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/serial_reg.h>
|
||||||
|
|
||||||
|
#include "hci_h4p.h"
|
||||||
|
|
||||||
|
static struct sk_buff_head *fw_q;
|
||||||
|
|
||||||
|
void hci_h4p_ti1273_parse_fw_event(struct hci_h4p_info *info,
|
||||||
|
struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct sk_buff *fw_skb;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (skb->data[5] != 0x00) {
|
||||||
|
dev_err(info->dev, "Firmware sending command failed 0x%.2x\n",
|
||||||
|
skb->data[5]);
|
||||||
|
info->fw_error = -EPROTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
fw_skb = skb_dequeue(fw_q);
|
||||||
|
if (fw_skb == NULL || info->fw_error) {
|
||||||
|
complete(&info->fw_completion);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
skb_queue_tail(&info->txq, fw_skb);
|
||||||
|
spin_lock_irqsave(&info->lock, flags);
|
||||||
|
hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
|
||||||
|
UART_IER_THRI);
|
||||||
|
spin_unlock_irqrestore(&info->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int hci_h4p_ti1273_send_fw(struct hci_h4p_info *info,
|
||||||
|
struct sk_buff_head *fw_queue)
|
||||||
|
{
|
||||||
|
struct sk_buff *skb;
|
||||||
|
unsigned long flags, time;
|
||||||
|
|
||||||
|
info->fw_error = 0;
|
||||||
|
|
||||||
|
BT_DBG("Sending firmware");
|
||||||
|
|
||||||
|
time = jiffies;
|
||||||
|
|
||||||
|
fw_q = fw_queue;
|
||||||
|
skb = skb_dequeue(fw_queue);
|
||||||
|
if (!skb)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
BT_DBG("Sending commands");
|
||||||
|
/* Check if this is bd_address packet */
|
||||||
|
init_completion(&info->fw_completion);
|
||||||
|
hci_h4p_smart_idle(info, 0);
|
||||||
|
skb_queue_tail(&info->txq, skb);
|
||||||
|
spin_lock_irqsave(&info->lock, flags);
|
||||||
|
hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
|
||||||
|
UART_IER_THRI);
|
||||||
|
spin_unlock_irqrestore(&info->lock, flags);
|
||||||
|
|
||||||
|
if (!wait_for_completion_timeout(&info->fw_completion,
|
||||||
|
msecs_to_jiffies(2000))) {
|
||||||
|
dev_err(info->dev, "No reply to fw command\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->fw_error) {
|
||||||
|
dev_err(info->dev, "FW error\n");
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
BT_DBG("Firmware sent in %d msecs",
|
||||||
|
jiffies_to_msecs(jiffies-time));
|
||||||
|
|
||||||
|
hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
|
||||||
|
hci_h4p_set_rts(info, 0);
|
||||||
|
hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
|
||||||
|
if (hci_h4p_wait_for_cts(info, 1, 100)) {
|
||||||
|
dev_err(info->dev,
|
||||||
|
"cts didn't go down after final speed change\n");
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
195
drivers/staging/nokia_h4p/nokia_fw.c
Normal file
195
drivers/staging/nokia_h4p/nokia_fw.c
Normal file
|
|
@ -0,0 +1,195 @@
|
||||||
|
/*
|
||||||
|
* This file is part of hci_h4p bluetooth driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005, 2006 Nokia Corporation.
|
||||||
|
*
|
||||||
|
* Contact: Ville Tervo <ville.tervo@nokia.com>
|
||||||
|
*
|
||||||
|
* 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 <linux/skbuff.h>
|
||||||
|
#include <linux/firmware.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
|
||||||
|
#include <net/bluetooth/bluetooth.h>
|
||||||
|
|
||||||
|
#include "hci_h4p.h"
|
||||||
|
|
||||||
|
static int fw_pos;
|
||||||
|
|
||||||
|
/* Firmware handling */
|
||||||
|
static int hci_h4p_open_firmware(struct hci_h4p_info *info,
|
||||||
|
const struct firmware **fw_entry)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
fw_pos = 0;
|
||||||
|
BT_DBG("Opening firmware man_id 0x%.2x ver_id 0x%.2x",
|
||||||
|
info->man_id, info->ver_id);
|
||||||
|
switch (info->man_id) {
|
||||||
|
case H4P_ID_TI1271:
|
||||||
|
switch (info->ver_id) {
|
||||||
|
case 0xe1:
|
||||||
|
err = request_firmware(fw_entry, FW_NAME_TI1271_PRELE,
|
||||||
|
info->dev);
|
||||||
|
break;
|
||||||
|
case 0xd1:
|
||||||
|
case 0xf1:
|
||||||
|
err = request_firmware(fw_entry, FW_NAME_TI1271_LE,
|
||||||
|
info->dev);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
err = request_firmware(fw_entry, FW_NAME_TI1271,
|
||||||
|
info->dev);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case H4P_ID_CSR:
|
||||||
|
err = request_firmware(fw_entry, FW_NAME_CSR, info->dev);
|
||||||
|
break;
|
||||||
|
case H4P_ID_BCM2048:
|
||||||
|
err = request_firmware(fw_entry, FW_NAME_BCM2048, info->dev);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(info->dev, "Invalid chip type\n");
|
||||||
|
*fw_entry = NULL;
|
||||||
|
err = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hci_h4p_close_firmware(const struct firmware *fw_entry)
|
||||||
|
{
|
||||||
|
release_firmware(fw_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read fw. Return length of the command. If no more commands in
|
||||||
|
* fw 0 is returned. In error case return value is negative.
|
||||||
|
*/
|
||||||
|
static int hci_h4p_read_fw_cmd(struct hci_h4p_info *info, struct sk_buff **skb,
|
||||||
|
const struct firmware *fw_entry, gfp_t how)
|
||||||
|
{
|
||||||
|
unsigned int cmd_len;
|
||||||
|
|
||||||
|
if (fw_pos >= fw_entry->size)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (fw_pos + 2 > fw_entry->size) {
|
||||||
|
dev_err(info->dev, "Corrupted firmware image 1\n");
|
||||||
|
return -EMSGSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_len = fw_entry->data[fw_pos++];
|
||||||
|
cmd_len += fw_entry->data[fw_pos++] << 8;
|
||||||
|
if (cmd_len == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (fw_pos + cmd_len > fw_entry->size) {
|
||||||
|
dev_err(info->dev, "Corrupted firmware image 2\n");
|
||||||
|
return -EMSGSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*skb = bt_skb_alloc(cmd_len, how);
|
||||||
|
if (!*skb) {
|
||||||
|
dev_err(info->dev, "Cannot reserve memory for buffer\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
memcpy(skb_put(*skb, cmd_len), &fw_entry->data[fw_pos], cmd_len);
|
||||||
|
|
||||||
|
fw_pos += cmd_len;
|
||||||
|
|
||||||
|
return (*skb)->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hci_h4p_read_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
|
||||||
|
{
|
||||||
|
const struct firmware *fw_entry = NULL;
|
||||||
|
struct sk_buff *skb = NULL;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = hci_h4p_open_firmware(info, &fw_entry);
|
||||||
|
if (err < 0 || !fw_entry)
|
||||||
|
goto err_clean;
|
||||||
|
|
||||||
|
while ((err = hci_h4p_read_fw_cmd(info, &skb, fw_entry, GFP_KERNEL))) {
|
||||||
|
if (err < 0 || !skb)
|
||||||
|
goto err_clean;
|
||||||
|
|
||||||
|
skb_queue_tail(fw_queue, skb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chip detection code does neg and alive stuff
|
||||||
|
* discard two first skbs */
|
||||||
|
skb = skb_dequeue(fw_queue);
|
||||||
|
if (!skb) {
|
||||||
|
err = -EMSGSIZE;
|
||||||
|
goto err_clean;
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
skb = skb_dequeue(fw_queue);
|
||||||
|
if (!skb) {
|
||||||
|
err = -EMSGSIZE;
|
||||||
|
goto err_clean;
|
||||||
|
}
|
||||||
|
kfree_skb(skb);
|
||||||
|
|
||||||
|
err_clean:
|
||||||
|
hci_h4p_close_firmware(fw_entry);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hci_h4p_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
switch (info->man_id) {
|
||||||
|
case H4P_ID_CSR:
|
||||||
|
err = hci_h4p_bc4_send_fw(info, fw_queue);
|
||||||
|
break;
|
||||||
|
case H4P_ID_TI1271:
|
||||||
|
err = hci_h4p_ti1273_send_fw(info, fw_queue);
|
||||||
|
break;
|
||||||
|
case H4P_ID_BCM2048:
|
||||||
|
err = hci_h4p_bcm_send_fw(info, fw_queue);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(info->dev, "Don't know how to send firmware\n");
|
||||||
|
err = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
switch (info->man_id) {
|
||||||
|
case H4P_ID_CSR:
|
||||||
|
hci_h4p_bc4_parse_fw_event(info, skb);
|
||||||
|
break;
|
||||||
|
case H4P_ID_TI1271:
|
||||||
|
hci_h4p_ti1273_parse_fw_event(info, skb);
|
||||||
|
break;
|
||||||
|
case H4P_ID_BCM2048:
|
||||||
|
hci_h4p_bcm_parse_fw_event(info, skb);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
dev_err(info->dev, "Don't know how to parse fw event\n");
|
||||||
|
info->fw_error = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
199
drivers/staging/nokia_h4p/nokia_uart.c
Normal file
199
drivers/staging/nokia_h4p/nokia_uart.c
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Nokia H4P bluetooth driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005, 2006 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 <linux/serial_reg.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
|
||||||
|
#include <linux/io.h>
|
||||||
|
|
||||||
|
#include "hci_h4p.h"
|
||||||
|
|
||||||
|
inline void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val)
|
||||||
|
{
|
||||||
|
__raw_writeb(val, info->uart_base + (offset << 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset)
|
||||||
|
{
|
||||||
|
return __raw_readb(info->uart_base + (offset << 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_h4p_set_rts(struct hci_h4p_info *info, int active)
|
||||||
|
{
|
||||||
|
u8 b;
|
||||||
|
|
||||||
|
b = hci_h4p_inb(info, UART_MCR);
|
||||||
|
if (active)
|
||||||
|
b |= UART_MCR_RTS;
|
||||||
|
else
|
||||||
|
b &= ~UART_MCR_RTS;
|
||||||
|
hci_h4p_outb(info, UART_MCR, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active,
|
||||||
|
int timeout_ms)
|
||||||
|
{
|
||||||
|
unsigned long timeout;
|
||||||
|
int state;
|
||||||
|
|
||||||
|
timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
||||||
|
for (;;) {
|
||||||
|
state = hci_h4p_inb(info, UART_MSR) & UART_MSR_CTS;
|
||||||
|
if (active) {
|
||||||
|
if (state)
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
if (!state)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (time_after(jiffies, timeout))
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
msleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which)
|
||||||
|
{
|
||||||
|
u8 lcr, b;
|
||||||
|
|
||||||
|
lcr = hci_h4p_inb(info, UART_LCR);
|
||||||
|
hci_h4p_outb(info, UART_LCR, 0xbf);
|
||||||
|
b = hci_h4p_inb(info, UART_EFR);
|
||||||
|
if (on)
|
||||||
|
b |= which;
|
||||||
|
else
|
||||||
|
b &= ~which;
|
||||||
|
hci_h4p_outb(info, UART_EFR, b);
|
||||||
|
hci_h4p_outb(info, UART_LCR, lcr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&info->lock, flags);
|
||||||
|
__hci_h4p_set_auto_ctsrts(info, on, which);
|
||||||
|
spin_unlock_irqrestore(&info->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed)
|
||||||
|
{
|
||||||
|
unsigned int divisor;
|
||||||
|
u8 lcr, mdr1;
|
||||||
|
|
||||||
|
BT_DBG("Setting speed %lu", speed);
|
||||||
|
|
||||||
|
if (speed >= 460800) {
|
||||||
|
divisor = UART_CLOCK / 13 / speed;
|
||||||
|
mdr1 = 3;
|
||||||
|
} else {
|
||||||
|
divisor = UART_CLOCK / 16 / speed;
|
||||||
|
mdr1 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure UART mode is disabled */
|
||||||
|
hci_h4p_outb(info, UART_OMAP_MDR1, 7);
|
||||||
|
|
||||||
|
lcr = hci_h4p_inb(info, UART_LCR);
|
||||||
|
hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB); /* Set DLAB */
|
||||||
|
hci_h4p_outb(info, UART_DLL, divisor & 0xff); /* Set speed */
|
||||||
|
hci_h4p_outb(info, UART_DLM, divisor >> 8);
|
||||||
|
hci_h4p_outb(info, UART_LCR, lcr);
|
||||||
|
|
||||||
|
/* Make sure UART mode is enabled */
|
||||||
|
hci_h4p_outb(info, UART_OMAP_MDR1, mdr1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hci_h4p_reset_uart(struct hci_h4p_info *info)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
/* Reset the UART */
|
||||||
|
hci_h4p_outb(info, UART_OMAP_SYSC, UART_SYSC_OMAP_RESET);
|
||||||
|
while (!(hci_h4p_inb(info, UART_OMAP_SYSS) & UART_SYSS_RESETDONE)) {
|
||||||
|
if (count++ > 100) {
|
||||||
|
dev_err(info->dev, "hci_h4p: UART reset timeout\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
udelay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_h4p_store_regs(struct hci_h4p_info *info)
|
||||||
|
{
|
||||||
|
u16 lcr = 0;
|
||||||
|
|
||||||
|
lcr = hci_h4p_inb(info, UART_LCR);
|
||||||
|
hci_h4p_outb(info, UART_LCR, 0xBF);
|
||||||
|
info->dll = hci_h4p_inb(info, UART_DLL);
|
||||||
|
info->dlh = hci_h4p_inb(info, UART_DLM);
|
||||||
|
info->efr = hci_h4p_inb(info, UART_EFR);
|
||||||
|
hci_h4p_outb(info, UART_LCR, lcr);
|
||||||
|
info->mdr1 = hci_h4p_inb(info, UART_OMAP_MDR1);
|
||||||
|
info->ier = hci_h4p_inb(info, UART_IER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_h4p_restore_regs(struct hci_h4p_info *info)
|
||||||
|
{
|
||||||
|
u16 lcr = 0;
|
||||||
|
|
||||||
|
hci_h4p_init_uart(info);
|
||||||
|
|
||||||
|
hci_h4p_outb(info, UART_OMAP_MDR1, 7);
|
||||||
|
lcr = hci_h4p_inb(info, UART_LCR);
|
||||||
|
hci_h4p_outb(info, UART_LCR, 0xBF);
|
||||||
|
hci_h4p_outb(info, UART_DLL, info->dll); /* Set speed */
|
||||||
|
hci_h4p_outb(info, UART_DLM, info->dlh);
|
||||||
|
hci_h4p_outb(info, UART_EFR, info->efr);
|
||||||
|
hci_h4p_outb(info, UART_LCR, lcr);
|
||||||
|
hci_h4p_outb(info, UART_OMAP_MDR1, info->mdr1);
|
||||||
|
hci_h4p_outb(info, UART_IER, info->ier);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hci_h4p_init_uart(struct hci_h4p_info *info)
|
||||||
|
{
|
||||||
|
u8 mcr, efr;
|
||||||
|
|
||||||
|
/* Enable and setup FIFO */
|
||||||
|
hci_h4p_outb(info, UART_OMAP_MDR1, 0x00);
|
||||||
|
|
||||||
|
hci_h4p_outb(info, UART_LCR, 0xbf);
|
||||||
|
efr = hci_h4p_inb(info, UART_EFR);
|
||||||
|
hci_h4p_outb(info, UART_EFR, UART_EFR_ECB);
|
||||||
|
hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);
|
||||||
|
mcr = hci_h4p_inb(info, UART_MCR);
|
||||||
|
hci_h4p_outb(info, UART_MCR, UART_MCR_TCRTLR);
|
||||||
|
hci_h4p_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO |
|
||||||
|
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT |
|
||||||
|
(3 << 6) | (0 << 4));
|
||||||
|
hci_h4p_outb(info, UART_LCR, 0xbf);
|
||||||
|
hci_h4p_outb(info, UART_TI752_TLR, 0xed);
|
||||||
|
hci_h4p_outb(info, UART_TI752_TCR, 0xef);
|
||||||
|
hci_h4p_outb(info, UART_EFR, efr);
|
||||||
|
hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);
|
||||||
|
hci_h4p_outb(info, UART_MCR, 0x00);
|
||||||
|
hci_h4p_outb(info, UART_LCR, UART_LCR_WLEN8);
|
||||||
|
hci_h4p_outb(info, UART_IER, UART_IER_RDI);
|
||||||
|
hci_h4p_outb(info, UART_OMAP_SYSC, (1 << 0) | (1 << 2) | (2 << 3));
|
||||||
|
}
|
||||||
38
include/linux/platform_data/bt-nokia-h4p.h
Normal file
38
include/linux/platform_data/bt-nokia-h4p.h
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Nokia H4P bluetooth driver
|
||||||
|
*
|
||||||
|
* Copyright (C) 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
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct hci_h4p_platform data - hci_h4p Platform data structure
|
||||||
|
*/
|
||||||
|
struct hci_h4p_platform_data {
|
||||||
|
int chip_type;
|
||||||
|
int bt_sysclk;
|
||||||
|
unsigned int bt_wakeup_gpio;
|
||||||
|
unsigned int host_wakeup_gpio;
|
||||||
|
unsigned int reset_gpio;
|
||||||
|
int reset_gpio_shared;
|
||||||
|
unsigned int uart_irq;
|
||||||
|
phys_addr_t uart_base;
|
||||||
|
const char *uart_iclk;
|
||||||
|
const char *uart_fclk;
|
||||||
|
void (*set_pm_limits)(struct device *dev, bool set);
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue