/* * Start the wireless host controller. * * Start device notification. * * Put hc into run state, set DNTS parameters. */ static int whc_start(struct usb_hcd *usb_hcd) { struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); struct whc *whc = wusbhc_to_whc(wusbhc); u8 bcid; int ret; mutex_lock(&wusbhc->mutex); le_writel(WUSBINTR_GEN_CMD_DONE | WUSBINTR_HOST_ERR | WUSBINTR_ASYNC_SCHED_SYNCED | WUSBINTR_DNTS_INT | WUSBINTR_ERR_INT | WUSBINTR_INT, whc->base + WUSBINTR); /* set cluster ID */ bcid = wusb_cluster_id_get(); ret = whc_set_cluster_id(whc, bcid); if (ret < 0) goto out; wusbhc->cluster_id = bcid; /* start HC */ whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN); usb_hcd->uses_new_polling = 1; set_bit(HCD_FLAG_POLL_RH, &usb_hcd->flags); usb_hcd->state = HC_STATE_RUNNING; out: mutex_unlock(&wusbhc->mutex); return ret; }
irqreturn_t whc_int_handler(struct usb_hcd *hcd) { struct wusbhc *wusbhc = usb_hcd_to_wusbhc(hcd); struct whc *whc = wusbhc_to_whc(wusbhc); u32 sts; sts = le_readl(whc->base + WUSBSTS); if (!(sts & WUSBSTS_INT_MASK)) return IRQ_NONE; le_writel(sts & WUSBSTS_INT_MASK, whc->base + WUSBSTS); if (sts & WUSBSTS_GEN_CMD_DONE) wake_up(&whc->cmd_wq); if (sts & WUSBSTS_HOST_ERR) dev_err(&whc->umc->dev, "FIXME: host system error\n"); if (sts & WUSBSTS_ASYNC_SCHED_SYNCED) wake_up(&whc->async_list_wq); if (sts & WUSBSTS_PERIODIC_SCHED_SYNCED) wake_up(&whc->periodic_list_wq); if (sts & WUSBSTS_DNTS_INT) queue_work(whc->workqueue, &whc->dn_work); if (sts & (WUSBSTS_INT | WUSBSTS_ERR_INT)) transfer_done(whc); return IRQ_HANDLED; }
int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm) { struct whc *whc = wusbhc_to_whc(wusbhc); if (stream_index >= 0) whc_write_wusbcmd(whc, WUSBCMD_WUSBSI_MASK, WUSBCMD_WUSBSI(stream_index)); return whc_do_gencmd(whc, WUSBGENCMDSTS_SET_MAS, 0, (void *)mas_bm, sizeof(*mas_bm)); }
int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle) { struct whc *whc = wusbhc_to_whc(wusbhc); u32 params; params = handle; return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_RM, params, NULL, 0); }
int whc_wusbhc_start(struct wusbhc *wusbhc) { struct whc *whc = wusbhc_to_whc(wusbhc); asl_start(whc); pzl_start(whc); return 0; }
/* * Set the number of Device Notification Time Slots (DNTS) and enable * device notifications. */ int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots) { struct whc *whc = wusbhc_to_whc(wusbhc); u32 dntsctrl; dntsctrl = WUSBDNTSCTRL_ACTIVE | WUSBDNTSCTRL_INTERVAL(interval) | WUSBDNTSCTRL_SLOTS(slots); le_writel(dntsctrl, whc->base + WUSBDNTSCTRL); return 0; }
int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt, u8 handle, struct wuie_hdr *wuie) { struct whc *whc = wusbhc_to_whc(wusbhc); u32 params; params = (interval << 24) | (repeat_cnt << 16) | (wuie->bLength << 8) | handle; return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_ADD, params, wuie, wuie->bLength); }
/** * whc_set_gtk - set the GTK for subsequent broadcast packets * * The GTK is stored in the last entry in the key table (the previous * N_DEVICES entries are for the per-device PTKs). */ int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid, const void *gtk, size_t key_size) { struct whc *whc = wusbhc_to_whc(wusbhc); int ret; mutex_lock(&whc->mutex); ret = whc_set_key(whc, whc->n_devices, tkid, gtk, key_size, true); mutex_unlock(&whc->mutex); return ret; }
void whc_wusbhc_stop(struct wusbhc *wusbhc, int delay) { struct whc *whc = wusbhc_to_whc(wusbhc); u32 stop_time, now_time; int ret; pzl_stop(whc); asl_stop(whc); now_time = le_readl(whc->base + WUSBTIME) & WUSBTIME_CHANNEL_TIME_MASK; stop_time = (now_time + ((delay * 8) << 7)) & 0x00ffffff; ret = whc_do_gencmd(whc, WUSBGENCMDSTS_CHAN_STOP, stop_time, NULL, 0); if (ret == 0) msleep(delay); }
static void whc_remove(struct umc_dev *umc) { struct usb_hcd *usb_hcd = dev_get_drvdata(&umc->dev); struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); struct whc *whc = wusbhc_to_whc(wusbhc); if (usb_hcd) { whc_dbg_clean_up(whc); wusbhc_b_destroy(wusbhc); usb_remove_hcd(usb_hcd); wusbhc_destroy(wusbhc); uwb_rc_put(wusbhc->uwb_rc); whc_clean_up(whc); usb_put_hcd(usb_hcd); } }
/* * Wait for all URBs to the endpoint to be completed, then delete the * qset. */ static void whc_endpoint_disable(struct usb_hcd *usb_hcd, struct usb_host_endpoint *ep) { struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); struct whc *whc = wusbhc_to_whc(wusbhc); struct whc_qset *qset; qset = ep->hcpriv; if (qset) { ep->hcpriv = NULL; if (usb_endpoint_xfer_bulk(&ep->desc) || usb_endpoint_xfer_control(&ep->desc)) asl_qset_delete(whc, qset); else pzl_qset_delete(whc, qset); } }
/* * Stop the wireless host controller. * * Stop device notification. * * Wait for pending transfer to stop? Put hc into stop state? */ static void whc_stop(struct usb_hcd *usb_hcd) { struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); struct whc *whc = wusbhc_to_whc(wusbhc); mutex_lock(&wusbhc->mutex); /* stop HC */ le_writel(0, whc->base + WUSBINTR); whc_write_wusbcmd(whc, WUSBCMD_RUN, 0); whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS, WUSBSTS_HCHALTED, WUSBSTS_HCHALTED, 100, "HC to halt"); wusb_cluster_id_put(wusbhc->cluster_id); mutex_unlock(&wusbhc->mutex); }
int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev) { struct whc *whc = wusbhc_to_whc(wusbhc); int idx = wusb_dev->port_idx; struct di_buf_entry *di = &whc->di_buf[idx]; int ret; mutex_lock(&whc->mutex); uwb_mas_bm_copy_le(di->availability_info, &wusb_dev->availability); di->addr_sec_info &= ~(WHC_DI_DISABLE | WHC_DI_DEV_ADDR_MASK); di->addr_sec_info |= WHC_DI_DEV_ADDR(wusb_dev->addr); ret = whc_update_di(whc, idx); mutex_unlock(&whc->mutex); return ret; }
/* * Remove a queued URB from the ASL or PZL. */ static int whc_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status) { struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); struct whc *whc = wusbhc_to_whc(wusbhc); int ret; switch (usb_pipetype(urb->pipe)) { case PIPE_INTERRUPT: ret = pzl_urb_dequeue(whc, urb, status); break; case PIPE_ISOCHRONOUS: ret = -ENOTSUPP; break; case PIPE_CONTROL: case PIPE_BULK: default: ret = asl_urb_dequeue(whc, urb, status); break; }; return ret; }
static void whc_endpoint_reset(struct usb_hcd *usb_hcd, struct usb_host_endpoint *ep) { struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); struct whc *whc = wusbhc_to_whc(wusbhc); struct whc_qset *qset; unsigned long flags; spin_lock_irqsave(&whc->lock, flags); qset = ep->hcpriv; if (qset) { qset->remove = 1; qset->reset = 1; if (usb_endpoint_xfer_bulk(&ep->desc) || usb_endpoint_xfer_control(&ep->desc)) queue_work(whc->workqueue, &whc->async_work); else queue_work(whc->workqueue, &whc->periodic_work); } spin_unlock_irqrestore(&whc->lock, flags); }
/* * Queue an URB to the ASL or PZL */ static int whc_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb, gfp_t mem_flags) { struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd); struct whc *whc = wusbhc_to_whc(wusbhc); int ret; switch (usb_pipetype(urb->pipe)) { case PIPE_INTERRUPT: ret = pzl_urb_enqueue(whc, urb, mem_flags); break; case PIPE_ISOCHRONOUS: dev_err(&whc->umc->dev, "isochronous transfers unsupported\n"); ret = -ENOTSUPP; break; case PIPE_CONTROL: case PIPE_BULK: default: ret = asl_urb_enqueue(whc, urb, mem_flags); break; }; return ret; }
/** * whc_set_ptk - set the PTK to use for a device. * * The index into the key table for this PTK is the same as the * device's port index. */ int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid, const void *ptk, size_t key_size) { struct whc *whc = wusbhc_to_whc(wusbhc); struct di_buf_entry *di = &whc->di_buf[port_idx]; int ret; mutex_lock(&whc->mutex); if (ptk) { ret = whc_set_key(whc, port_idx, tkid, ptk, key_size, false); if (ret) goto out; di->addr_sec_info &= ~WHC_DI_KEY_IDX_MASK; di->addr_sec_info |= WHC_DI_SECURE | WHC_DI_KEY_IDX(port_idx); } else di->addr_sec_info &= ~WHC_DI_SECURE; ret = whc_update_di(whc, port_idx); out: mutex_unlock(&whc->mutex); return ret; }
static int whc_probe(struct umc_dev *umc) { int ret = -ENOMEM; struct usb_hcd *usb_hcd; struct wusbhc *wusbhc = NULL; struct whc *whc = NULL; struct device *dev = &umc->dev; usb_hcd = usb_create_hcd(&whc_hc_driver, dev, "whci"); if (usb_hcd == NULL) { dev_err(dev, "unable to create hcd\n"); goto error; } usb_hcd->wireless = 1; usb_hcd->self.sg_tablesize = 2048; /* somewhat arbitrary */ wusbhc = usb_hcd_to_wusbhc(usb_hcd); whc = wusbhc_to_whc(wusbhc); whc->umc = umc; ret = whc_init(whc); if (ret) goto error; wusbhc->dev = dev; wusbhc->uwb_rc = uwb_rc_get_by_grandpa(umc->dev.parent); if (!wusbhc->uwb_rc) { ret = -ENODEV; dev_err(dev, "cannot get radio controller\n"); goto error; } if (whc->n_devices > USB_MAXCHILDREN) { dev_warn(dev, "USB_MAXCHILDREN too low for WUSB adapter (%u ports)\n", whc->n_devices); wusbhc->ports_max = USB_MAXCHILDREN; } else wusbhc->ports_max = whc->n_devices; wusbhc->mmcies_max = whc->n_mmc_ies; wusbhc->start = whc_wusbhc_start; wusbhc->stop = whc_wusbhc_stop; wusbhc->mmcie_add = whc_mmcie_add; wusbhc->mmcie_rm = whc_mmcie_rm; wusbhc->dev_info_set = whc_dev_info_set; wusbhc->bwa_set = whc_bwa_set; wusbhc->set_num_dnts = whc_set_num_dnts; wusbhc->set_ptk = whc_set_ptk; wusbhc->set_gtk = whc_set_gtk; ret = wusbhc_create(wusbhc); if (ret) goto error_wusbhc_create; ret = usb_add_hcd(usb_hcd, whc->umc->irq, IRQF_SHARED); if (ret) { dev_err(dev, "cannot add HCD: %d\n", ret); goto error_usb_add_hcd; } ret = wusbhc_b_create(wusbhc); if (ret) { dev_err(dev, "WUSBHC phase B setup failed: %d\n", ret); goto error_wusbhc_b_create; } whc_dbg_init(whc); return 0; error_wusbhc_b_create: usb_remove_hcd(usb_hcd); error_usb_add_hcd: wusbhc_destroy(wusbhc); error_wusbhc_create: uwb_rc_put(wusbhc->uwb_rc); error: whc_clean_up(whc); if (usb_hcd) usb_put_hcd(usb_hcd); return ret; }