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);
}
Beispiel #2
0
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);
}