📄 if_sdio.c
字号:
} 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) { timeout = jiffies + HZ; while (1) { status = sdio_readb(card->func, IF_SDIO_STATUS, &ret); if (ret) goto release; if ((status & IF_SDIO_IO_RDY) && (status & IF_SDIO_DL_RDY)) break; if (time_after(jiffies, timeout)) { ret = -ETIMEDOUT; goto release; } mdelay(1); } 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, (chunk_size + 31) / 32 * 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_set_block_size(card->func, 0); sdio_release_host(card->func); kfree(chunk_buffer);release_fw: release_firmware(fw);out: if (ret) lbs_pr_err("failed to load firmware\n"); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret;}static int if_sdio_prog_firmware(struct if_sdio_card *card){ int ret; u16 scratch; lbs_deb_enter(LBS_DEB_SDIO); sdio_claim_host(card->func); scratch = if_sdio_read_scratch(card, &ret); sdio_release_host(card->func); if (ret) goto out; if (scratch == IF_SDIO_FIRMWARE_OK) { lbs_deb_sdio("firmware already loaded\n"); goto success; } ret = if_sdio_prog_helper(card); if (ret) goto out; ret = if_sdio_prog_real(card); if (ret) goto out;success: ret = 0;out: lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret;}/*******************************************************************//* Libertas callbacks *//*******************************************************************/static int if_sdio_host_to_card(wlan_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. */ size = nb + 4; if ((size > card->func->cur_blksize) || (size > 512)) { size = (size + card->func->cur_blksize - 1) / card->func->cur_blksize * card->func->cur_blksize; } 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); schedule_work(&card->packet_worker); ret = 0;out: lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret;}static int if_sdio_get_int_status(wlan_private *priv, u8 *ireg){ struct if_sdio_card *card; lbs_deb_enter(LBS_DEB_SDIO); card = priv->card; *ireg = card->int_cause; card->int_cause = 0; lbs_deb_leave(LBS_DEB_SDIO); return 0;}static int if_sdio_read_event_cause(wlan_private *priv){ struct if_sdio_card *card; lbs_deb_enter(LBS_DEB_SDIO); card = priv->card; priv->adapter->eventcause = card->event; lbs_deb_leave(LBS_DEB_SDIO); 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) 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. */ if (cause & IF_SDIO_H_INT_DNLD) { if ((card->priv->dnld_sent == DNLD_DATA_SENT) && (card->priv->adapter->connect_status == LIBERTAS_CONNECTED)) netif_wake_queue(card->priv->dev); card->priv->dnld_sent = DNLD_RES_RECEIVED; } 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; wlan_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 = 4; break; } } if (i == func->card->num_info) { lbs_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; spin_lock_init(&card->lock); INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker); for (i = 0;i < ARRAY_SIZE(if_sdio_models);i++) { if (card->model == if_sdio_models[i].model) break; } if (i == ARRAY_SIZE(if_sdio_models)) { lbs_pr_err("unkown card model 0x%x\n", card->model); ret = -ENODEV; goto free; } card->helper = if_sdio_models[i].helper; card->firmware = if_sdio_models[i].firmware; if (libertas_helper_name) { lbs_deb_sdio("overriding helper firmware: %s\n", libertas_helper_name); card->helper = libertas_helper_name; } if (libertas_fw_name) { lbs_deb_sdio("overriding firmware: %s\n", libertas_fw_name); card->firmware = libertas_fw_name; } sdio_claim_host(func); ret = sdio_enable_func(func); if (ret) goto release; ret = sdio_claim_irq(func, if_sdio_interrupt); if (ret) goto disable; card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret); if (ret) goto release_int; card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8; if (ret) goto release_int; card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16; if (ret) goto release_int; sdio_release_host(func); 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); ret = if_sdio_prog_firmware(card); if (ret) goto reclaim; priv = libertas_add_card(card, &func->dev); if (!priv) { ret = -ENOMEM; goto reclaim; } card->priv = priv; priv->card = card; priv->hw_host_to_card = if_sdio_host_to_card; priv->hw_get_int_status = if_sdio_get_int_status; priv->hw_read_event_cause = if_sdio_read_event_cause; priv->adapter->fw_ready = 1; /* * Enable interrupts now that everything is set up */ sdio_claim_host(func); sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret); sdio_release_host(func); if (ret) goto reclaim; ret = libertas_start_card(priv); if (ret) goto err_activate_card;out: lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret;err_activate_card: flush_scheduled_work(); free_netdev(priv->dev); kfree(priv->adapter);reclaim: sdio_claim_host(func);release_int: sdio_release_irq(func);disable: sdio_disable_func(func);release: sdio_release_host(func);free: 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); card->priv->adapter->surpriseremoved = 1; lbs_deb_sdio("call remove card\n"); libertas_stop_card(card->priv); libertas_remove_card(card->priv); flush_scheduled_work(); sdio_claim_host(func); sdio_release_irq(func); sdio_disable_func(func); sdio_release_host(func); while (card->packets) { packet = card->packets; card->packets = card->packets->next; kfree(packet); } kfree(card); lbs_deb_leave(LBS_DEB_SDIO);}static struct sdio_driver if_sdio_driver = { .name = "libertas_sdio", .id_table = if_sdio_ids, .probe = if_sdio_probe, .remove = if_sdio_remove,};/*******************************************************************//* Module functions *//*******************************************************************/static int 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); lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret); return ret;}static void if_sdio_exit_module(void){ lbs_deb_enter(LBS_DEB_SDIO); 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");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -