Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/iwn/if_iwn.c
Show First 20 Lines • Show All 470 Lines • ▼ Show 20 Lines | sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | | ||||
(rid != 0 ? 0 : RF_SHAREABLE)); | (rid != 0 ? 0 : RF_SHAREABLE)); | ||||
if (sc->irq == NULL) { | if (sc->irq == NULL) { | ||||
device_printf(dev, "can't map interrupt\n"); | device_printf(dev, "can't map interrupt\n"); | ||||
error = ENOMEM; | error = ENOMEM; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
IWN_LOCK_INIT(sc); | IWN_LOCK_INIT(sc); | ||||
IWN_CMD_LOCK_INIT(sc); | |||||
/* Read hardware revision and attach. */ | /* Read hardware revision and attach. */ | ||||
sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> IWN_HW_REV_TYPE_SHIFT) | sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> IWN_HW_REV_TYPE_SHIFT) | ||||
& IWN_HW_REV_TYPE_MASK; | & IWN_HW_REV_TYPE_MASK; | ||||
sc->subdevice_id = pci_get_subdevice(dev); | sc->subdevice_id = pci_get_subdevice(dev); | ||||
/* | /* | ||||
* 4965 versus 5000 and later have different methods. | * 4965 versus 5000 and later have different methods. | ||||
▲ Show 20 Lines • Show All 951 Lines • ▼ Show 20 Lines | iwn_detach(device_t dev) | ||||
if (sc->sc_cdev) { | if (sc->sc_cdev) { | ||||
destroy_dev(sc->sc_cdev); | destroy_dev(sc->sc_cdev); | ||||
sc->sc_cdev = NULL; | sc->sc_cdev = NULL; | ||||
} | } | ||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n", __func__); | DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n", __func__); | ||||
IWN_LOCK_DESTROY(sc); | IWN_LOCK_DESTROY(sc); | ||||
IWN_CMD_LOCK_DESTROY(sc); | |||||
return 0; | return 0; | ||||
} | } | ||||
static int | static int | ||||
iwn_shutdown(device_t dev) | iwn_shutdown(device_t dev) | ||||
{ | { | ||||
struct iwn_softc *sc = device_get_softc(dev); | struct iwn_softc *sc = device_get_softc(dev); | ||||
▲ Show 20 Lines • Show All 661 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
/* Disable interrupts. */ | /* Disable interrupts. */ | ||||
IWN_WRITE(sc, IWN_INT_MASK, 0); | IWN_WRITE(sc, IWN_INT_MASK, 0); | ||||
/* Reset ICT table. */ | /* Reset ICT table. */ | ||||
memset(sc->ict, 0, IWN_ICT_SIZE); | memset(sc->ict, 0, IWN_ICT_SIZE); | ||||
sc->ict_cur = 0; | sc->ict_cur = 0; | ||||
bus_dmamap_sync(sc->ict_dma.tag, sc->ict_dma.map, | |||||
BUS_DMASYNC_PREWRITE); | |||||
/* Set physical address of ICT table (4KB aligned). */ | /* Set physical address of ICT table (4KB aligned). */ | ||||
DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__); | DPRINTF(sc, IWN_DEBUG_RESET, "%s: enabling ICT\n", __func__); | ||||
IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE | | IWN_WRITE(sc, IWN_DRAM_INT_TBL, IWN_DRAM_INT_TBL_ENABLE | | ||||
IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12); | IWN_DRAM_INT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> 12); | ||||
/* Enable periodic RX interrupt. */ | /* Enable periodic RX interrupt. */ | ||||
sc->int_mask |= IWN_INT_RX_PERIODIC; | sc->int_mask |= IWN_INT_RX_PERIODIC; | ||||
/* Switch to ICT interrupt mode in driver. */ | /* Switch to ICT interrupt mode in driver. */ | ||||
▲ Show 20 Lines • Show All 1,964 Lines • ▼ Show 20 Lines | iwn_intr(void *arg) | ||||
IWN_LOCK(sc); | IWN_LOCK(sc); | ||||
/* Disable interrupts. */ | /* Disable interrupts. */ | ||||
IWN_WRITE(sc, IWN_INT_MASK, 0); | IWN_WRITE(sc, IWN_INT_MASK, 0); | ||||
/* Read interrupts from ICT (fast) or from registers (slow). */ | /* Read interrupts from ICT (fast) or from registers (slow). */ | ||||
if (sc->sc_flags & IWN_FLAG_USE_ICT) { | if (sc->sc_flags & IWN_FLAG_USE_ICT) { | ||||
bus_dmamap_sync(sc->ict_dma.tag, sc->ict_dma.map, | |||||
BUS_DMASYNC_POSTREAD); | |||||
tmp = 0; | tmp = 0; | ||||
while (sc->ict[sc->ict_cur] != 0) { | while (sc->ict[sc->ict_cur] != 0) { | ||||
tmp |= sc->ict[sc->ict_cur]; | tmp |= sc->ict[sc->ict_cur]; | ||||
sc->ict[sc->ict_cur] = 0; /* Acknowledge. */ | sc->ict[sc->ict_cur] = 0; /* Acknowledge. */ | ||||
sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT; | sc->ict_cur = (sc->ict_cur + 1) % IWN_ICT_COUNT; | ||||
} | } | ||||
tmp = le32toh(tmp); | tmp = le32toh(tmp); | ||||
if (tmp == 0xffffffff) /* Shouldn't happen. */ | if (tmp == 0xffffffff) /* Shouldn't happen. */ | ||||
▲ Show 20 Lines • Show All 519 Lines • ▼ Show 20 Lines | iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) | ||||
for (i = 1; i <= nsegs; i++) { | for (i = 1; i <= nsegs; i++) { | ||||
desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr)); | desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr)); | ||||
desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) | | desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) | | ||||
seg->ds_len << 4); | seg->ds_len << 4); | ||||
seg++; | seg++; | ||||
} | } | ||||
bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); | bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); | ||||
bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, | bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map, | ||||
BUS_DMASYNC_PREWRITE); | BUS_DMASYNC_PREWRITE); | ||||
bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, | bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, | ||||
BUS_DMASYNC_PREWRITE); | BUS_DMASYNC_PREWRITE); | ||||
/* Update TX scheduler. */ | /* Update TX scheduler. */ | ||||
if (ring->qid >= sc->firstaggqueue) | if (ring->qid >= sc->firstaggqueue) | ||||
ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); | ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); | ||||
▲ Show 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, | ||||
for (i = 1; i <= nsegs; i++) { | for (i = 1; i <= nsegs; i++) { | ||||
desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr)); | desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr)); | ||||
desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) | | desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) | | ||||
seg->ds_len << 4); | seg->ds_len << 4); | ||||
seg++; | seg++; | ||||
} | } | ||||
bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); | bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); | ||||
bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, | bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map, | ||||
BUS_DMASYNC_PREWRITE); | BUS_DMASYNC_PREWRITE); | ||||
bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, | bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, | ||||
BUS_DMASYNC_PREWRITE); | BUS_DMASYNC_PREWRITE); | ||||
/* Update TX scheduler. */ | /* Update TX scheduler. */ | ||||
if (ring->qid >= sc->firstaggqueue) | if (ring->qid >= sc->firstaggqueue) | ||||
ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); | ops->update_sched(sc, ring->qid, ring->cur, tx->id, totlen); | ||||
▲ Show 20 Lines • Show All 265 Lines • ▼ Show 20 Lines | iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) | ||||
struct iwn_tx_cmd *cmd; | struct iwn_tx_cmd *cmd; | ||||
struct mbuf *m; | struct mbuf *m; | ||||
bus_addr_t paddr; | bus_addr_t paddr; | ||||
int totlen, error; | int totlen, error; | ||||
int cmd_queue_num; | int cmd_queue_num; | ||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); | DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); | ||||
if (async == 0) | if (async == 0) | ||||
avos: AFAIR, this is wrong; it should be just locked everywhere. | |||||
gonzoAuthorUnsubmitted Not Done Inline ActionsYes, I thought so and tried to fix accordingly but it may affect performance in non-subtle way and I have only one iwn(4) card to test all code paths. On the other hand - having dedicated mutex for command queue is very non-intrusive fix that makes sure iwn_cmd accesses command slots sequentially gonzo: Yes, I thought so and tried to fix accordingly but it may affect performance in non-subtle way… | |||||
avosUnsubmitted Not Done Inline ActionsAnd, in theory, can also allow to cleanup locking logic (like in rS280071 ) P.S. Probably, writes to IWN_HBUS_TARG_WRPTR register from other threads should be protected too? avos: And, in theory, can also allow to cleanup locking logic (like in rS280071 )
P.S. Probably… | |||||
IWN_LOCK_ASSERT(sc); | IWN_LOCK_ASSERT(sc); | ||||
IWN_CMD_LOCK(sc); | |||||
if (sc->sc_flags & IWN_FLAG_PAN_SUPPORT) | if (sc->sc_flags & IWN_FLAG_PAN_SUPPORT) | ||||
cmd_queue_num = IWN_PAN_CMD_QUEUE; | cmd_queue_num = IWN_PAN_CMD_QUEUE; | ||||
else | else | ||||
cmd_queue_num = IWN_CMD_QUEUE_NUM; | cmd_queue_num = IWN_CMD_QUEUE_NUM; | ||||
ring = &sc->txq[cmd_queue_num]; | ring = &sc->txq[cmd_queue_num]; | ||||
desc = &ring->desc[ring->cur]; | desc = &ring->desc[ring->cur]; | ||||
data = &ring->data[ring->cur]; | data = &ring->data[ring->cur]; | ||||
totlen = 4 + size; | totlen = 4 + size; | ||||
if (size > sizeof cmd->data) { | if (size > sizeof cmd->data) { | ||||
/* Command is too large to fit in a descriptor. */ | /* Command is too large to fit in a descriptor. */ | ||||
if (totlen > MCLBYTES) | if (totlen > MCLBYTES) { | ||||
return EINVAL; | error = EINVAL; | ||||
goto out; | |||||
} | |||||
m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); | m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); | ||||
if (m == NULL) | if (m == NULL) { | ||||
return ENOMEM; | error = ENOMEM; | ||||
goto out; | |||||
} | |||||
cmd = mtod(m, struct iwn_tx_cmd *); | cmd = mtod(m, struct iwn_tx_cmd *); | ||||
error = bus_dmamap_load(ring->data_dmat, data->map, cmd, | error = bus_dmamap_load(ring->data_dmat, data->map, cmd, | ||||
totlen, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); | totlen, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); | ||||
if (error != 0) { | if (error != 0) { | ||||
m_freem(m); | m_freem(m); | ||||
return error; | goto out; | ||||
} | } | ||||
data->m = m; | data->m = m; | ||||
} else { | } else { | ||||
cmd = &ring->cmd[ring->cur]; | cmd = &ring->cmd[ring->cur]; | ||||
paddr = data->cmd_paddr; | paddr = data->cmd_paddr; | ||||
} | } | ||||
cmd->code = code; | cmd->code = code; | ||||
Show All 9 Lines | iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) | ||||
DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n", | DPRINTF(sc, IWN_DEBUG_CMD, "%s: %s (0x%x) flags %d qid %d idx %d\n", | ||||
__func__, iwn_intr_str(cmd->code), cmd->code, | __func__, iwn_intr_str(cmd->code), cmd->code, | ||||
cmd->flags, cmd->qid, cmd->idx); | cmd->flags, cmd->qid, cmd->idx); | ||||
if (size > sizeof cmd->data) { | if (size > sizeof cmd->data) { | ||||
bus_dmamap_sync(ring->data_dmat, data->map, | bus_dmamap_sync(ring->data_dmat, data->map, | ||||
BUS_DMASYNC_PREWRITE); | BUS_DMASYNC_PREWRITE); | ||||
} else { | } else { | ||||
bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, | bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map, | ||||
BUS_DMASYNC_PREWRITE); | BUS_DMASYNC_PREWRITE); | ||||
} | } | ||||
bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, | bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, | ||||
BUS_DMASYNC_PREWRITE); | BUS_DMASYNC_PREWRITE); | ||||
/* Kick command ring. */ | /* Kick command ring. */ | ||||
ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; | ring->cur = (ring->cur + 1) % IWN_TX_RING_COUNT; | ||||
IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); | IWN_WRITE(sc, IWN_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); | ||||
DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); | DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); | ||||
return async ? 0 : msleep(desc, &sc->sc_mtx, PCATCH, "iwncmd", hz); | |||||
out: | |||||
IWN_CMD_UNLOCK(sc); | |||||
if (!async && (error == 0)) | |||||
error = msleep(desc, &sc->sc_mtx, PCATCH, "iwncmd", hz); | |||||
return (error); | |||||
} | } | ||||
static int | static int | ||||
iwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) | iwn4965_add_node(struct iwn_softc *sc, struct iwn_node_info *node, int async) | ||||
{ | { | ||||
struct iwn4965_node_info hnode; | struct iwn4965_node_info hnode; | ||||
caddr_t src, dst; | caddr_t src, dst; | ||||
▲ Show 20 Lines • Show All 3,846 Lines • Show Last 20 Lines |
AFAIR, this is wrong; it should be just locked everywhere.