int smschar_register(void) { dev_t devno = MKDEV(smschar_major, smschar_minor); int rc; sms_info("registering device major=%d minor=%d", smschar_major, smschar_minor); if (smschar_major) { rc = register_chrdev_region(devno, SMSCHAR_NR_DEVS, "smschar"); } else { rc = alloc_chrdev_region(&devno, smschar_minor, SMSCHAR_NR_DEVS, "smschar"); smschar_major = MAJOR(devno); } if (rc < 0) { sms_warn("smschar: can't get major %d", smschar_major); return rc; } init_waitqueue_head(&g_pnp_event); kmutex_init(&g_smschar_pollwait_lock); return smscore_register_hotplug(smschar_hotplug); }
static void smssdio_work_thread(struct work_struct *arg) { int ret, isr; struct smscore_buffer_t *cb; struct SmsMsgHdr_S *hdr; size_t size; struct smssdio_device *smsdev = container_of(arg, struct smssdio_device, work_thread); struct sdio_func *sdfunc = smsdev->func; /* * The interrupt register has no defined meaning. It is just * a way of turning of the level triggered interrupt. */ sdio_claim_host(smsdev->func); isr = sdio_readb(smsdev->func, SMSSDIO_INT, &ret); if (ret) { sms_err("Got error reading interrupt status=%d, isr=%d\n", ret, isr); isr = sdio_readb(smsdev->func, SMSSDIO_INT, &ret); if (ret) { sms_err("Second read also failed, try to recover\n"); sdio_release_host(smsdev->func); sdfunc = kmemdup(smsdev->func, sizeof(struct sdio_func), GFP_KERNEL); if (!sdfunc) { sms_err("Out of memory!!!"); return; } sdfunc->num = 0; sdio_claim_host(sdfunc); sdio_writeb(sdfunc, 2, SMSSDIO_CCCR, &ret); sms_err("Read ISR status (write returned) %d\n", ret); isr = sdio_readb(smsdev->func, SMSSDIO_INT, &ret); sms_err("Read returned ret=%d, isr=%d\n", ret, isr); sdio_writeb(sdfunc, 0, SMSSDIO_CCCR, &ret); sdio_release_host(sdfunc); kfree(sdfunc); sms_err("Recovered, but this transaction is lost."); return; } sms_err("Second read succeed status=%d, isr=%d (continue)\n", ret, isr); } if (smsdev->split_cb == NULL) { cb = smscore_getbuffer(smsdev->coredev); if (!cb) { sms_err("Unable to allocate data buffer!\n"); sdio_release_host(smsdev->func); return; } ret = sdio_memcpy_fromio(smsdev->func, cb->p, SMSSDIO_DATA, SMSSDIO_BLOCK_SIZE); if (ret) { sms_warn("Error %d reading initial block, " "continue with sequence.\n", ret); } hdr = cb->p; if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) { smsdev->split_cb = cb; sdio_release_host(smsdev->func); return; } if (hdr->msgLength > smsdev->func->cur_blksize) size = hdr->msgLength - smsdev->func->cur_blksize; else size = 0; } else { cb = smsdev->split_cb; hdr = cb->p; size = hdr->msgLength - sizeof(struct SmsMsgHdr_S); smsdev->split_cb = NULL; } if (size) { void *buffer; buffer = cb->p + (hdr->msgLength - size); size = ALIGN(size, SMSSDIO_BLOCK_SIZE); BUG_ON(smsdev->func->cur_blksize != SMSSDIO_BLOCK_SIZE); /* * First attempt to transfer all of it in one go... */ ret = sdio_memcpy_fromio(smsdev->func, buffer, SMSSDIO_DATA, size); if (ret && ret != -EINVAL) { smscore_putbuffer(smsdev->coredev, cb); sms_err("Error %d reading data from card!\n", ret); sdio_release_host(smsdev->func); return; } /* * ..then fall back to one block at a time if that is * not possible... * * (we have to do this manually because of the * problem with the "increase address" bit) */ if (ret == -EINVAL) { while (size) { ret = sdio_memcpy_fromio(smsdev->func, buffer, SMSSDIO_DATA, smsdev->func->cur_blksize); if (ret) { smscore_putbuffer(smsdev->coredev, cb); sms_err("Error %d reading " "data from card!\n", ret); sdio_release_host(smsdev->func); return; } buffer += smsdev->func->cur_blksize; if (size > smsdev->func->cur_blksize) size -= smsdev->func->cur_blksize; else size = 0; } } } sdio_release_host(smsdev->func); cb->size = hdr->msgLength; cb->offset = 0; smscore_onresponse(smsdev->coredev, cb); }