Merge "defconfig: 9615: Enable SPS for MMC" into msm-3.0
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 2f092c8..3265ffa 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -5447,13 +5447,19 @@
/* Deassert MM SW_RESET_ALL signal. */
writel_relaxed(0, SW_RESET_ALL_REG);
+ /*
+ * Some bits are only used on either 8960 or 8064 and are marked as
+ * reserved bits on the other SoC. Writing to these reserved bits
+ * should have no effect.
+ */
/* Initialize MM AHB registers: Enable the FPB clock and disable HW
* gating for all clocks. Also set VFE_AHB's FORCE_CORE_ON bit to
* prevent its memory from being collapsed when the clock is halted.
* The sleep and wake-up delays are set to safe values. */
rmwreg(0x00000003, AHB_EN_REG, 0x6C000103);
writel_relaxed(0x000007F9, AHB_EN2_REG);
- rmwreg(0x00000000, AHB_EN3_REG, 0x00000001);
+ if (cpu_is_apq8064())
+ rmwreg(0x00000000, AHB_EN3_REG, 0x00000001);
/* Deassert all locally-owned MM AHB resets. */
rmwreg(0, SW_RESET_AHB_REG, 0xFFF7DFFF);
@@ -5467,7 +5473,8 @@
rmwreg(0x3027FCFF, MAXI_EN2_REG, 0x3A3FFFFF);
rmwreg(0x0027FCFF, MAXI_EN3_REG, 0x003FFFFF);
rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF);
- rmwreg(0x009FE4FF, MAXI_EN5_REG, 0x01FFEFFF);
+ if (cpu_is_apq8064())
+ rmwreg(0x009FE4FF, MAXI_EN5_REG, 0x01FFEFFF);
rmwreg(0x000003C7, SAXI_EN_REG, 0x00003FFF);
/* Initialize MM CC registers: Set MM FORCE_CORE_ON bits so that core
@@ -5479,22 +5486,26 @@
rmwreg(0x80FF0000, DSI2_BYTE_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, DSI_PIXEL_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, DSI2_PIXEL_CC_REG, 0xE0FF0010);
- rmwreg(0x80FF0000, GFX2D0_CC_REG, 0xE0FF0010);
- rmwreg(0x80FF0000, GFX2D1_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, GFX3D_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, IJPEG_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, JPEGD_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, MDP_CC_REG, 0xE1FF0010);
rmwreg(0x80FF0000, MDP_LUT_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, ROT_CC_REG, 0xE0FF0010);
- rmwreg(0x80FF0000, TV_CC_REG, 0xE1FFC010);
rmwreg(0x000004FF, TV_CC2_REG, 0x000007FF);
rmwreg(0xC0FF0000, VCODEC_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, VFE_CC_REG, 0xE0FF4010);
rmwreg(0x800000FF, VFE_CC2_REG, 0xE00000FF);
rmwreg(0x80FF0000, VPE_CC_REG, 0xE0FF0010);
- if (cpu_is_apq8064())
+ if (cpu_is_msm8960()) {
+ rmwreg(0x80FF0000, GFX2D0_CC_REG, 0xE0FF0010);
+ rmwreg(0x80FF0000, GFX2D1_CC_REG, 0xE0FF0010);
+ rmwreg(0x80FF0000, TV_CC_REG, 0xE1FFC010);
+ }
+ if (cpu_is_apq8064()) {
+ rmwreg(0x00000000, TV_CC_REG, 0x00004010);
rmwreg(0x80FF0000, VCAP_CC_REG, 0xE0FF1010);
+ }
/*
* Initialize USB_HS_HCLK_FS registers: Set FORCE_C_ON bits so that
@@ -5502,8 +5513,10 @@
* and wake-up value to max.
*/
rmwreg(0x0000004F, USB_HS1_HCLK_FS_REG, 0x0000007F);
- rmwreg(0x0000004F, USB_HS3_HCLK_FS_REG, 0x0000007F);
- rmwreg(0x0000004F, USB_HS4_HCLK_FS_REG, 0x0000007F);
+ if (cpu_is_apq8064()) {
+ rmwreg(0x0000004F, USB_HS3_HCLK_FS_REG, 0x0000007F);
+ rmwreg(0x0000004F, USB_HS4_HCLK_FS_REG, 0x0000007F);
+ }
/* De-assert MM AXI resets to all hardware blocks. */
writel_relaxed(0, SW_RESET_AXI_REG);
@@ -5529,7 +5542,8 @@
writel_relaxed(BIT(15), PDM_CLK_NS_REG);
/* Source SLIMBus xo src from slimbus reference clock */
- writel_relaxed(0x3, SLIMBUS_XO_SRC_CLK_CTL_REG);
+ if (cpu_is_msm8960())
+ writel_relaxed(0x3, SLIMBUS_XO_SRC_CLK_CTL_REG);
/* Source the dsi_byte_clks from the DSI PHY PLLs */
rmwreg(0x1, DSI1_BYTE_NS_REG, 0x7);
diff --git a/arch/arm/mach-msm/sdio_al.c b/arch/arm/mach-msm/sdio_al.c
index 8267fad..ace437b 100644
--- a/arch/arm/mach-msm/sdio_al.c
+++ b/arch/arm/mach-msm/sdio_al.c
@@ -108,6 +108,7 @@
* packet) rx data.
*/
#define DEFAULT_READ_THRESHOLD (1024)
+#define LOW_LATENCY_THRESHOLD (1)
/* Extra bytes to ensure getting the rx threshold interrupt on stream channels
when restoring the threshold after sleep */
@@ -119,7 +120,6 @@
#define THRESHOLD_DISABLE_VAL (0xFFFFFFFF)
-
/** Mailbox polling time for packet channels */
#define DEFAULT_POLL_DELAY_MSEC 10
/** Mailbox polling time for streaming channels */
@@ -986,7 +986,7 @@
continue;
}
if (ch->is_packet_mode == false) {
- ch->read_threshold = 1;
+ ch->read_threshold = LOW_LATENCY_THRESHOLD;
set_pipe_threshold(sdio_al_dev,
ch->rx_pipe_index,
ch->read_threshold);
@@ -1156,13 +1156,22 @@
sdio_al_dev->host->index);
}
ch->read_avail = read_avail;
- /* Restore default thresh for non packet channels */
+
+ /*
+ * Restore default thresh for non packet channels.
+ * in case it IS low latency channel then read_threshold
+ * and def_read_threshold are both
+ * LOW_LATENCY_THRESHOLD
+ */
if ((ch->read_threshold != ch->def_read_threshold) &&
(read_avail >= ch->threshold_change_cnt)) {
- ch->read_threshold = ch->def_read_threshold;
- set_pipe_threshold(sdio_al_dev,
- ch->rx_pipe_index,
- ch->read_threshold);
+ if (!ch->is_low_latency_ch) {
+ ch->read_threshold =
+ ch->def_read_threshold;
+ set_pipe_threshold(sdio_al_dev,
+ ch->rx_pipe_index,
+ ch->read_threshold);
+ }
}
}
@@ -1968,14 +1977,18 @@
goto exit_err;
}
- /* Aggregation up to 90% of the maximum size */
- ch->read_threshold = (ch_config->max_rx_threshold * 9) / 10;
+ ch->read_threshold = LOW_LATENCY_THRESHOLD;
+ ch->is_low_latency_ch = ch_config->is_low_latency_ch;
/* Threshold on 50% of the maximum size , sdioc uses double-buffer */
ch->write_threshold = (ch_config->max_tx_threshold * 5) / 10;
ch->threshold_change_cnt = ch->ch_config.max_rx_threshold -
ch->read_threshold + THRESHOLD_CHANGE_EXTRA_BYTES;
- ch->def_read_threshold = ch->read_threshold;
+ if (ch->is_low_latency_ch)
+ ch->def_read_threshold = LOW_LATENCY_THRESHOLD;
+ else /* Aggregation up to 90% of the maximum size */
+ ch->def_read_threshold = (ch_config->max_rx_threshold * 9) / 10;
+
ch->is_packet_mode = ch_config->is_packet_mode;
if (!ch->is_packet_mode) {
ch->poll_delay_msec = DEFAULT_POLL_DELAY_NOPACKET_MSEC;
diff --git a/arch/arm/mach-msm/sdio_al_private.h b/arch/arm/mach-msm/sdio_al_private.h
index f352499..36d9ec1 100644
--- a/arch/arm/mach-msm/sdio_al_private.h
+++ b/arch/arm/mach-msm/sdio_al_private.h
@@ -66,7 +66,8 @@
u32 is_host_ok_to_sleep;
u32 is_packet_mode;
u32 peer_operation;
- u32 reserved[24];
+ u32 is_low_latency_ch;
+ u32 reserved[23];
};
@@ -173,6 +174,7 @@
int min_write_avail;
int poll_delay_msec;
int is_packet_mode;
+ int is_low_latency_ch;
struct peer_sdioc_channel_config ch_config;
diff --git a/drivers/bluetooth/hci_smd.c b/drivers/bluetooth/hci_smd.c
index 31634ff..66bab6b 100644
--- a/drivers/bluetooth/hci_smd.c
+++ b/drivers/bluetooth/hci_smd.c
@@ -302,6 +302,7 @@
break;
}
+ kfree_skb(skb);
wake_unlock(&hs.wake_lock_tx);
return ret;
}
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 165bbbf..0f8d474 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -329,6 +329,12 @@
KGSL_PWR_WARN(device, "state -> SUSPEND, device %d\n",
device->id);
break;
+ case KGSL_STATE_SLUMBER:
+ INIT_COMPLETION(device->hwaccess_gate);
+ device->state = KGSL_STATE_SUSPEND;
+ KGSL_PWR_WARN(device, "state -> SUSPEND, device %d\n",
+ device->id);
+ break;
default:
KGSL_PWR_ERR(device, "suspend fail, device %d\n",
device->id);
@@ -355,28 +361,16 @@
KGSL_PWR_WARN(device, "resume start\n");
mutex_lock(&device->mutex);
if (device->state == KGSL_STATE_SUSPEND) {
- device->requested_state = KGSL_STATE_ACTIVE;
- kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL);
- status = device->ftbl->start(device, 0);
- if (status == 0) {
- device->state = KGSL_STATE_ACTIVE;
- KGSL_PWR_WARN(device,
- "state -> ACTIVE, device %d\n",
- device->id);
- } else {
- KGSL_PWR_ERR(device,
- "resume failed, device %d\n",
- device->id);
- device->state = KGSL_STATE_INIT;
- goto end;
- }
+ device->state = KGSL_STATE_SLUMBER;
+ status = 0;
+ KGSL_PWR_WARN(device,
+ "state -> SLUMBER, device %d\n",
+ device->id);
complete_all(&device->hwaccess_gate);
}
device->requested_state = KGSL_STATE_NONE;
-end:
mutex_unlock(&device->mutex);
- kgsl_check_idle(device);
KGSL_PWR_WARN(device, "resume end\n");
return status;
}
@@ -417,9 +411,12 @@
{
struct kgsl_device *device = container_of(h,
struct kgsl_device, display_off);
+ KGSL_PWR_WARN(device, "early suspend start\n");
mutex_lock(&device->mutex);
- kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL);
+ device->requested_state = KGSL_STATE_SLUMBER;
+ kgsl_pwrctrl_sleep(device);
mutex_unlock(&device->mutex);
+ KGSL_PWR_WARN(device, "early suspend end\n");
}
EXPORT_SYMBOL(kgsl_early_suspend_driver);
@@ -442,9 +439,13 @@
{
struct kgsl_device *device = container_of(h,
struct kgsl_device, display_off);
+ KGSL_PWR_WARN(device, "late resume start\n");
mutex_lock(&device->mutex);
- kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
+ kgsl_pwrctrl_wake(device);
+ device->pwrctrl.restore_slumber = 0;
mutex_unlock(&device->mutex);
+ kgsl_check_idle(device);
+ KGSL_PWR_WARN(device, "late resume end\n");
}
EXPORT_SYMBOL(kgsl_late_resume_driver);
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 2f369ed..b6e79b8 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -46,6 +46,7 @@
#define KGSL_STATE_SUSPEND 0x00000010
#define KGSL_STATE_HUNG 0x00000020
#define KGSL_STATE_DUMP_AND_RECOVER 0x00000040
+#define KGSL_STATE_SLUMBER 0x00000080
#define KGSL_GRAPHICS_MEMORY_LOW_WATERMARK 0x1000000
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 84f2b33..b4f22ee 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -583,7 +583,8 @@
mutex_lock(&device->mutex);
if (device->ftbl->isidle(device) &&
- (device->requested_state != KGSL_STATE_SLEEP))
+ (device->requested_state != KGSL_STATE_SLEEP) &&
+ (device->requested_state != KGSL_STATE_SLUMBER))
kgsl_pwrscale_idle(device);
if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) {
@@ -622,7 +623,8 @@
void kgsl_pre_hwaccess(struct kgsl_device *device)
{
BUG_ON(!mutex_is_locked(&device->mutex));
- if (device->state & (KGSL_STATE_SLEEP | KGSL_STATE_NAP))
+ if (device->state & (KGSL_STATE_SLEEP | KGSL_STATE_NAP |
+ KGSL_STATE_SLUMBER))
kgsl_pwrctrl_wake(device);
}
EXPORT_SYMBOL(kgsl_pre_hwaccess);
@@ -640,8 +642,40 @@
wait_for_completion(&device->recovery_gate);
mutex_lock(&device->mutex);
}
- }
+}
+static int
+_slumber(struct kgsl_device *device)
+{
+ int status = -EINVAL;
+ if (!device)
+ return -EINVAL;
+ KGSL_PWR_WARN(device, "Slumber start\n");
+
+ device->requested_state = KGSL_STATE_SLUMBER;
+ del_timer(&device->idle_timer);
+ switch (device->state) {
+ case KGSL_STATE_ACTIVE:
+ /* Wait for the device to become idle */
+ device->ftbl->idle(device, KGSL_TIMEOUT_DEFAULT);
+ case KGSL_STATE_NAP:
+ case KGSL_STATE_SLEEP:
+ device->ftbl->suspend_context(device);
+ device->ftbl->stop(device);
+ device->state = KGSL_STATE_SLUMBER;
+ device->pwrctrl.restore_slumber = 1;
+ KGSL_PWR_WARN(device, "state -> SLUMBER, device %d\n",
+ device->id);
+ break;
+ default:
+ break;
+ }
+ status = 0;
+ /* Don't set requested state to NONE
+ It's done in kgsl_pwrctrl_sleep*/
+ KGSL_PWR_WARN(device, "Done going to slumber\n");
+ return status;
+}
/******************************************************************/
/* Caller must hold the device mutex. */
@@ -656,13 +690,24 @@
goto nap;
} else if (device->requested_state == KGSL_STATE_SLEEP) {
if (device->state == KGSL_STATE_NAP ||
- device->ftbl->isidle(device))
- goto sleep;
+ device->ftbl->isidle(device)) {
+ if (!device->pwrctrl.restore_slumber)
+ goto sleep;
+ else
+ goto slumber;
+ }
+ } else if (device->requested_state == KGSL_STATE_SLUMBER) {
+ if (device->ftbl->isidle(device))
+ goto slumber;
}
device->requested_state = KGSL_STATE_NONE;
return -EBUSY;
+
+slumber:
+ _slumber(device);
+
sleep:
kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF);
@@ -694,6 +739,24 @@
}
EXPORT_SYMBOL(kgsl_pwrctrl_sleep);
+static int
+_wake_from_slumber(struct kgsl_device *device)
+{
+ int status = -EINVAL;
+ if (!device)
+ return -EINVAL;
+
+ KGSL_PWR_WARN(device, "wake from slumber start\n");
+
+ device->requested_state = KGSL_STATE_ACTIVE;
+ kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL);
+ status = device->ftbl->start(device, 0);
+ device->requested_state = KGSL_STATE_NONE;
+
+ KGSL_PWR_WARN(device, "Done waking from slumber\n");
+ return status;
+}
+
/******************************************************************/
/* Caller must hold the device mutex. */
void kgsl_pwrctrl_wake(struct kgsl_device *device)
@@ -701,6 +764,9 @@
if (device->state == KGSL_STATE_SUSPEND)
return;
+ if (device->state == KGSL_STATE_SLUMBER)
+ _wake_from_slumber(device);
+
if (device->state != KGSL_STATE_NAP) {
kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON);
kgsl_pwrscale_wake(device);
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 127a19b..17dab76 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -56,6 +56,7 @@
const char *src_clk_name;
s64 time;
struct kgsl_busy busy;
+ unsigned int restore_slumber;
};
void kgsl_pwrctrl_clk(struct kgsl_device *device, int state);
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 9017706..9d268d1 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -1699,6 +1699,9 @@
if (mReq->req.status != -EALREADY)
return -EINVAL;
+ /* clean speculative fetches on req->ptr->token */
+ mb();
+
if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0)
return -EBUSY;
@@ -2074,7 +2077,6 @@
continue; /* not configured */
if (hw_test_and_clear_complete(i)) {
- udelay(200);
err = isr_tr_complete_low(mEp);
if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
if (err > 0) /* needs status phase */
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index aa6aeb9..b67803a 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -708,6 +708,12 @@
__le16 sco_max_pkt;
} __packed;
+#define HCI_OP_WRITE_CURRENT_IAC_LAP 0x0c3a
+struct hci_cp_write_current_iac_lap {
+ __u8 num_current_iac;
+ __u8 lap[6];
+} __packed;
+
#define HCI_OP_WRITE_INQUIRY_MODE 0x0c45
#define HCI_MAX_EIR_LENGTH 240
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 5e855dbf..208c157 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -90,6 +90,8 @@
__u8 major;
__u8 minor;
} __packed;
+#define MGMT_MAJOR_CLASS_MASK 0x1F
+#define MGMT_MAJOR_CLASS_LIMITED 0x20
#define MGMT_OP_SET_SERVICE_CACHE 0x000C
struct mgmt_cp_set_service_cache {
@@ -214,6 +216,8 @@
bdaddr_t bdaddr;
} __packed;
+#define MGMT_OP_SET_LIMIT_DISCOVERABLE 0x001F
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index a328db9..ea72708 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -409,6 +409,124 @@
return err;
}
+static u8 get_service_classes(struct hci_dev *hdev)
+{
+ struct list_head *p;
+ u8 val = 0;
+
+ list_for_each(p, &hdev->uuids) {
+ struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
+
+ val |= uuid->svc_hint;
+ }
+
+ return val;
+}
+
+static int update_class(struct hci_dev *hdev)
+{
+ u8 cod[3];
+
+ BT_DBG("%s", hdev->name);
+
+ if (test_bit(HCI_SERVICE_CACHE, &hdev->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;
+
+ return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
+}
+
+static int set_limited_discoverable(struct sock *sk, u16 index,
+ unsigned char *data, u16 len)
+{
+ struct mgmt_mode *cp;
+ struct hci_dev *hdev;
+ struct pending_cmd *cmd;
+ struct hci_cp_write_current_iac_lap dcp;
+ int update_cod;
+ int err = 0;
+ /* General Inquiry LAP: 0x9E8B33, Limited Inquiry LAP: 0x9E8B00 */
+ u8 lap[] = { 0x33, 0x8b, 0x9e, 0x00, 0x8b, 0x9e };
+
+ cp = (void *) data;
+
+ BT_DBG("hci%u discoverable: %d", index, cp->val);
+
+ if (!cp || len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ ENODEV);
+
+ hci_dev_lock(hdev);
+
+ if (!test_bit(HCI_UP, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ ENETDOWN);
+ goto failed;
+ }
+
+ if (mgmt_pending_find(MGMT_OP_SET_LIMIT_DISCOVERABLE, index)) {
+ err = cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ EBUSY);
+ goto failed;
+ }
+
+ if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
+ test_bit(HCI_PSCAN, &hdev->flags)) {
+ err = cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+ EALREADY);
+ goto failed;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_LIMIT_DISCOVERABLE, index, data,
+ len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ memset(&dcp, 0, sizeof(dcp));
+ dcp.num_current_iac = cp->val ? 2 : 1;
+ memcpy(&dcp.lap, lap, dcp.num_current_iac * 3);
+ update_cod = 1;
+
+ if (cp->val) {
+ if (hdev->major_class & MGMT_MAJOR_CLASS_LIMITED)
+ update_cod = 0;
+ hdev->major_class |= MGMT_MAJOR_CLASS_LIMITED;
+ } else {
+ if (!(hdev->major_class & MGMT_MAJOR_CLASS_LIMITED))
+ update_cod = 0;
+ hdev->major_class &= ~MGMT_MAJOR_CLASS_LIMITED;
+ }
+
+ if (update_cod)
+ err = update_class(hdev);
+
+ if (err >= 0)
+ err = hci_send_cmd(hdev, HCI_OP_WRITE_CURRENT_IAC_LAP,
+ sizeof(dcp), &dcp);
+
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+failed:
+ hci_dev_unlock(hdev);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
@@ -751,39 +869,6 @@
return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
}
-static u8 get_service_classes(struct hci_dev *hdev)
-{
- struct list_head *p;
- u8 val = 0;
-
- list_for_each(p, &hdev->uuids) {
- struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
-
- val |= uuid->svc_hint;
- }
-
- return val;
-}
-
-static int update_class(struct hci_dev *hdev)
-{
- u8 cod[3];
-
- BT_DBG("%s", hdev->name);
-
- if (test_bit(HCI_SERVICE_CACHE, &hdev->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;
-
- return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
-}
-
static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
{
struct mgmt_cp_add_uuid *cp;
@@ -912,7 +997,8 @@
hci_dev_lock(hdev);
- hdev->major_class = cp->major;
+ hdev->major_class &= ~MGMT_MAJOR_CLASS_MASK;
+ hdev->major_class |= cp->major & MGMT_MAJOR_CLASS_MASK;
hdev->minor_class = cp->minor;
err = update_class(hdev);
@@ -2111,6 +2197,10 @@
case MGMT_OP_SET_DISCOVERABLE:
err = set_discoverable(sk, index, buf + sizeof(*hdr), len);
break;
+ case MGMT_OP_SET_LIMIT_DISCOVERABLE:
+ err = set_limited_discoverable(sk, index, buf + sizeof(*hdr),
+ len);
+ break;
case MGMT_OP_SET_CONNECTABLE:
err = set_connectable(sk, index, buf + sizeof(*hdr), len);
break;