// SPDX-License-Identifier: GPL-2.0-only /* * Remote VUB300 SDIO/SDmem Host Controller Driver * * Copyright (C) 2010 Elan Digital Systems Limited * * based on USB Skeleton driver - 2.2 * * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) * * VUB300: is a USB 2.0 client device with a single SDIO/SDmem/MMC slot * Any SDIO/SDmem/MMC device plugged into the VUB300 will appear, * by virtue of this driver, to have been plugged into a local * SDIO host controller, similar to, say, a PCI Ricoh controller * This is because this kernel device driver is both a USB 2.0 * client device driver AND an MMC host controller driver. Thus * if there is an existing driver for the inserted SDIO/SDmem/MMC * device then that driver will be used by the kernel to manage * the device in exactly the same fashion as if it had been * directly plugged into, say, a local pci bus Ricoh controller * * RANT: this driver was written using a display 128x48 - converting it * to a line width of 80 makes it very difficult to support. In * particular functions have been broken down into sub functions * and the original meaningful names have been shortened into * cryptic ones. * The problem is that executing a fragment of code subject to * two conditions means an indentation of 24, thus leaving only * 56 characters for a C statement. And that is quite ridiculous! * * Data types: data passed to/from the VUB300 is fixed to a number of * bits and driver data fields reflect that limit by using * u8, u16, u32
*/ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> #include <linux/kref.h> #include <linux/uaccess.h> #include <linux/usb.h> #include <linux/mutex.h> #include <linux/mmc/host.h> #include <linux/mmc/card.h> #include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_ids.h> #include <linux/workqueue.h> #include <linux/ctype.h> #include <linux/firmware.h> #include <linux/scatterlist.h>
staticbool limit_speed_to_24_MHz;
module_param(limit_speed_to_24_MHz, bool, 0644);
MODULE_PARM_DESC(limit_speed_to_24_MHz, "Limit Max SDIO Clock Speed to 24 MHz");
staticbool pad_input_to_usb_pkt;
module_param(pad_input_to_usb_pkt, bool, 0644);
MODULE_PARM_DESC(pad_input_to_usb_pkt, "Pad USB data input transfers to whole USB Packet");
staticvoid vub300_delete(struct kref *kref)
{ /* kref callback - softirq */ struct vub300_mmc_host *vub300 = kref_to_vub300_mmc_host(kref);
usb_free_urb(vub300->command_out_urb);
vub300->command_out_urb = NULL;
usb_free_urb(vub300->command_res_urb);
vub300->command_res_urb = NULL;
usb_put_dev(vub300->udev); /* * and hence also frees vub300 * which is contained at the end of struct mmc
*/
}
staticvoid vub300_queue_cmnd_work(struct vub300_mmc_host *vub300)
{
kref_get(&vub300->kref); if (queue_work(cmndworkqueue, &vub300->cmndwork)) { /* * then the cmndworkqueue was not previously * running and the above get ref is obvious * required and will be put when the thread * terminates by a specific call
*/
} else { /* * the cmndworkqueue was already running from * a previous invocation and thus to keep the * kref counts correct we must undo the get
*/
kref_put(&vub300->kref, vub300_delete);
}
}
staticvoid vub300_queue_poll_work(struct vub300_mmc_host *vub300, int delay)
{
kref_get(&vub300->kref); if (queue_delayed_work(pollworkqueue, &vub300->pollwork, delay)) { /* * then the pollworkqueue was not previously * running and the above get ref is obvious * required and will be put when the thread * terminates by a specific call
*/
} else { /* * the pollworkqueue was already running from * a previous invocation and thus to keep the * kref counts correct we must undo the get
*/
kref_put(&vub300->kref, vub300_delete);
}
}
staticvoid vub300_queue_dead_work(struct vub300_mmc_host *vub300)
{
kref_get(&vub300->kref); if (queue_work(deadworkqueue, &vub300->deadwork)) { /* * then the deadworkqueue was not previously * running and the above get ref is obvious * required and will be put when the thread * terminates by a specific call
*/
} else { /* * the deadworkqueue was already running from * a previous invocation and thus to keep the * kref counts correct we must undo the get
*/
kref_put(&vub300->kref, vub300_delete);
}
}
staticvoid check_vub300_port_status(struct vub300_mmc_host *vub300)
{ /* * cmd_mutex is held by vub300_pollwork_thread, * vub300_deadwork_thread or vub300_cmndwork_thread
*/ int retval;
retval =
usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0),
GET_SYSTEM_PORT_STATUS,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x0000, 0x0000, &vub300->system_port_status, sizeof(vub300->system_port_status), 1000); if (sizeof(vub300->system_port_status) == retval)
new_system_port_status(vub300);
}
staticvoid __vub300_irqpoll_response(struct vub300_mmc_host *vub300)
{ /* cmd_mutex is held by vub300_pollwork_thread */ if (vub300->command_res_urb->actual_length == 0) return;
switch (vub300->resp.common.header_type) { case RESPONSE_INTERRUPT:
mutex_lock(&vub300->irq_mutex); if (vub300->irq_enabled)
mmc_signal_sdio_irq(vub300->mmc); else
vub300->irqs_queued += 1;
vub300->irq_disabled = 1;
mutex_unlock(&vub300->irq_mutex); break; case RESPONSE_ERROR: if (vub300->resp.error.error_code == SD_ERROR_NO_DEVICE)
check_vub300_port_status(vub300); break; case RESPONSE_STATUS:
vub300->system_port_status = vub300->resp.status;
new_system_port_status(vub300); if (!vub300->card_present)
vub300_queue_poll_work(vub300, HZ / 5); break; case RESPONSE_IRQ_DISABLED:
{ int offloaded_data_length = vub300->resp.common.header_size - 3; int register_count = offloaded_data_length >> 3; int ri = 0; while (register_count--) {
add_offloaded_reg(vub300, &vub300->resp.irq.reg[ri]);
ri += 1;
}
mutex_lock(&vub300->irq_mutex); if (vub300->irq_enabled)
mmc_signal_sdio_irq(vub300->mmc); else
vub300->irqs_queued += 1;
vub300->irq_disabled = 1;
mutex_unlock(&vub300->irq_mutex); break;
} case RESPONSE_IRQ_ENABLED:
{ int offloaded_data_length = vub300->resp.common.header_size - 3; int register_count = offloaded_data_length >> 3; int ri = 0; while (register_count--) {
add_offloaded_reg(vub300, &vub300->resp.irq.reg[ri]);
ri += 1;
}
mutex_lock(&vub300->irq_mutex); if (vub300->irq_enabled)
mmc_signal_sdio_irq(vub300->mmc); else
vub300->irqs_queued += 1;
vub300->irq_disabled = 0;
mutex_unlock(&vub300->irq_mutex); break;
} case RESPONSE_NO_INTERRUPT:
vub300_queue_poll_work(vub300, 1); break; default: break;
}
}
staticvoid __do_poll(struct vub300_mmc_host *vub300)
{ /* cmd_mutex is held by vub300_pollwork_thread */ unsignedlong commretval;
mod_timer(&vub300->inactivity_timer, jiffies + HZ);
init_completion(&vub300->irqpoll_complete);
send_irqpoll(vub300);
commretval = wait_for_completion_timeout(&vub300->irqpoll_complete,
msecs_to_jiffies(500)); if (vub300->usb_transport_fail) { /* no need to do anything */
} elseif (commretval == 0) {
vub300->usb_timed_out = 1;
usb_kill_urb(vub300->command_out_urb);
usb_kill_urb(vub300->command_res_urb);
} else { /* commretval > 0 */
__vub300_irqpoll_response(vub300);
}
}
/* this thread runs only when the driver * is trying to poll the device for an IRQ
*/ staticvoid vub300_pollwork_thread(struct work_struct *work)
{ /* NOT irq */ struct vub300_mmc_host *vub300 = container_of(work, struct vub300_mmc_host, pollwork.work); if (!vub300->interface) {
kref_put(&vub300->kref, vub300_delete); return;
}
mutex_lock(&vub300->cmd_mutex); if (vub300->cmd) {
vub300_queue_poll_work(vub300, 1);
} elseif (!vub300->card_present) { /* no need to do anything */
} else { /* vub300->card_present */
mutex_lock(&vub300->irq_mutex); if (!vub300->irq_enabled) {
mutex_unlock(&vub300->irq_mutex);
} elseif (vub300->irqs_queued) {
vub300->irqs_queued -= 1;
mmc_signal_sdio_irq(vub300->mmc);
mod_timer(&vub300->inactivity_timer, jiffies + HZ);
mutex_unlock(&vub300->irq_mutex);
} else { /* NOT vub300->irqs_queued */
mutex_unlock(&vub300->irq_mutex);
__do_poll(vub300);
}
}
mutex_unlock(&vub300->cmd_mutex);
kref_put(&vub300->kref, vub300_delete);
}
staticvoid vub300_deadwork_thread(struct work_struct *work)
{ /* NOT irq */ struct vub300_mmc_host *vub300 =
container_of(work, struct vub300_mmc_host, deadwork); if (!vub300->interface) {
kref_put(&vub300->kref, vub300_delete); return;
}
mutex_lock(&vub300->cmd_mutex); if (vub300->cmd) { /* * a command got in as the inactivity * timer expired - so we just let the * processing of the command show if * the device is dead
*/
} elseif (vub300->card_present) {
check_vub300_port_status(vub300);
} elseif (vub300->mmc && vub300->mmc->card) { /* * the MMC core must not have responded * to the previous indication - lets * hope that it eventually does so we * will just ignore this for now
*/
} else {
check_vub300_port_status(vub300);
}
mod_timer(&vub300->inactivity_timer, jiffies + HZ);
mutex_unlock(&vub300->cmd_mutex);
kref_put(&vub300->kref, vub300_delete);
}
staticint vub300_response_error(u8 error_code)
{ switch (error_code) { case SD_ERROR_PIO_TIMEOUT: case SD_ERROR_1BIT_TIMEOUT: case SD_ERROR_4BIT_TIMEOUT: return -ETIMEDOUT; case SD_ERROR_STAT_DATA: case SD_ERROR_OVERRUN: case SD_ERROR_STAT_CMD: case SD_ERROR_STAT_CMD_TIMEOUT: case SD_ERROR_SDCRDY_STUCK: case SD_ERROR_UNHANDLED: case SD_ERROR_1BIT_CRC_WRONG: case SD_ERROR_4BIT_CRC_WRONG: case SD_ERROR_1BIT_CRC_ERROR: case SD_ERROR_4BIT_CRC_ERROR: case SD_ERROR_NO_CMD_ENDBIT: case SD_ERROR_NO_1BIT_DATEND: case SD_ERROR_NO_4BIT_DATEND: case SD_ERROR_1BIT_DATA_TIMEOUT: case SD_ERROR_4BIT_DATA_TIMEOUT: case SD_ERROR_1BIT_UNEXPECTED_TIMEOUT: case SD_ERROR_4BIT_UNEXPECTED_TIMEOUT: return -EILSEQ; case 33: return -EILSEQ; case SD_ERROR_ILLEGAL_COMMAND: return -EINVAL; case SD_ERROR_NO_DEVICE: return -ENOMEDIUM; default: return -ENODEV;
}
}
staticvoid command_res_completed(struct urb *urb)
{ /* urb completion handler - hardirq */ struct vub300_mmc_host *vub300 = (struct vub300_mmc_host *)urb->context; if (urb->status) { /* we have to let the initiator handle the error */
} elseif (vub300->command_res_urb->actual_length == 0) { /* * we have seen this happen once or twice and * we suspect a buggy USB host controller
*/
} elseif (!vub300->data) { /* this means that the command (typically CMD52) succeeded */
} elseif (vub300->resp.common.header_type != 0x02) { /* * this is an error response from the VUB300 chip * and we let the initiator handle it
*/
} elseif (vub300->urb) {
vub300->cmd->error =
vub300_response_error(vub300->resp.error.error_code);
usb_unlink_urb(vub300->urb);
} else {
vub300->cmd->error =
vub300_response_error(vub300->resp.error.error_code);
usb_sg_cancel(&vub300->sg_request);
}
complete(&vub300->command_complete); /* got_response_in */
}
staticvoid command_out_completed(struct urb *urb)
{ /* urb completion handler - hardirq */ struct vub300_mmc_host *vub300 = (struct vub300_mmc_host *)urb->context; if (urb->status) {
complete(&vub300->command_complete);
} else { int ret; unsignedint pipe =
usb_rcvbulkpipe(vub300->udev, vub300->cmnd_res_ep);
usb_fill_bulk_urb(vub300->command_res_urb, vub300->udev, pipe,
&vub300->resp, sizeof(vub300->resp),
command_res_completed, vub300);
vub300->command_res_urb->actual_length = 0;
ret = usb_submit_urb(vub300->command_res_urb, GFP_ATOMIC); if (ret == 0) { /* * the urb completion handler will call * our completion handler
*/
} else { /* * and thus we only call it directly * when it will not be called
*/
complete(&vub300->command_complete);
}
}
}
for (i = 0; i < ARRAY_SIZE(vub300->fbs); i++)
vub300->fbs[i] = 512;
/* * set up the endpoint information * * use the first pair of bulk-in and bulk-out * endpoints for Command/Response+Interrupt * * use the second pair of bulk-in and bulk-out * endpoints for Data In/Out
*/
vub300->large_usb_packets = 0;
iface_desc = interface->cur_altsetting; for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { struct usb_endpoint_descriptor *endpoint =
&iface_desc->endpoint[i].desc;
dev_info(&vub300->udev->dev, "vub300 testing %s EndPoint(%d) %02X\n",
usb_endpoint_is_bulk_in(endpoint) ? "BULK IN" :
usb_endpoint_is_bulk_out(endpoint) ? "BULK OUT" : "UNKNOWN", i, endpoint->bEndpointAddress); if (endpoint->wMaxPacketSize > 64)
vub300->large_usb_packets = 1; if (usb_endpoint_is_bulk_in(endpoint)) { if (!vub300->cmnd_res_ep) {
vub300->cmnd_res_ep =
endpoint->bEndpointAddress;
} elseif (!vub300->data_inp_ep) {
vub300->data_inp_ep =
endpoint->bEndpointAddress;
} else {
dev_warn(&vub300->udev->dev, "ignoring" " unexpected bulk_in endpoint");
}
} elseif (usb_endpoint_is_bulk_out(endpoint)) { if (!vub300->cmnd_out_ep) {
vub300->cmnd_out_ep =
endpoint->bEndpointAddress;
} elseif (!vub300->data_out_ep) {
vub300->data_out_ep =
endpoint->bEndpointAddress;
} else {
dev_warn(&vub300->udev->dev, "ignoring" " unexpected bulk_out endpoint");
}
} else {
dev_warn(&vub300->udev->dev, "vub300 ignoring EndPoint(%d) %02X", i,
endpoint->bEndpointAddress);
}
} if (vub300->cmnd_res_ep && vub300->cmnd_out_ep &&
vub300->data_inp_ep && vub300->data_out_ep) {
dev_info(&vub300->udev->dev, "vub300 %s packets" " using EndPoints %02X %02X %02X %02X\n",
vub300->large_usb_packets ? "LARGE" : "SMALL",
vub300->cmnd_out_ep, vub300->cmnd_res_ep,
vub300->data_out_ep, vub300->data_inp_ep); /* we have the expected EndPoints */
} else {
dev_err(&vub300->udev->dev, "Could not find two sets of bulk-in/out endpoint pairs\n");
retval = -EINVAL; goto error4;
}
retval =
usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0),
GET_HC_INF0,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x0000, 0x0000, &vub300->hc_info, sizeof(vub300->hc_info), 1000); if (retval < 0) goto error4;
retval =
usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0),
SET_ROM_WAIT_STATES,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
firmware_rom_wait_states, 0x0000, NULL, 0, 1000); if (retval < 0) goto error4;
dev_info(&vub300->udev->dev, "operating_mode = %s %s %d MHz %s %d byte USB packets\n",
(mmc->caps & MMC_CAP_SDIO_IRQ) ? "IRQs" : "POLL",
(mmc->caps & MMC_CAP_4_BIT_DATA) ? "4-bit" : "1-bit",
mmc->f_max / 1000000,
pad_input_to_usb_pkt ? "padding input data to" : "with",
vub300->large_usb_packets ? 512 : 64);
retval =
usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0),
GET_SYSTEM_PORT_STATUS,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0x0000, 0x0000, &vub300->system_port_status, sizeof(vub300->system_port_status), 1000); if (retval < 0) { goto error4;
} elseif (sizeof(vub300->system_port_status) == retval) {
vub300->card_present =
(0x0001 & vub300->system_port_status.port_flags) ? 1 : 0;
vub300->read_only =
(0x0010 & vub300->system_port_status.port_flags) ? 1 : 0;
} else {
retval = -EINVAL; goto error4;
}
usb_set_intfdata(interface, vub300);
INIT_DELAYED_WORK(&vub300->pollwork, vub300_pollwork_thread);
INIT_WORK(&vub300->cmndwork, vub300_cmndwork_thread);
INIT_WORK(&vub300->deadwork, vub300_deadwork_thread);
kref_init(&vub300->kref);
timer_setup(&vub300->sg_transfer_timer, vub300_sg_timed_out, 0);
kref_get(&vub300->kref);
timer_setup(&vub300->inactivity_timer,
vub300_inactivity_timer_expired, 0);
vub300->inactivity_timer.expires = jiffies + HZ;
add_timer(&vub300->inactivity_timer); if (vub300->card_present)
dev_info(&vub300->udev->dev, "USB vub300 remote SDIO host controller[%d]" "connected with SD/SDIO card inserted\n",
interface_to_InterfaceNumber(interface)); else
dev_info(&vub300->udev->dev, "USB vub300 remote SDIO host controller[%d]" "connected with no SD/SDIO card inserted\n",
interface_to_InterfaceNumber(interface));
retval = mmc_add_host(mmc); if (retval) goto error6;
return 0;
error6:
timer_delete_sync(&vub300->inactivity_timer); /* * and hence also frees vub300 * which is contained at the end of struct mmc
*/
error4:
usb_free_urb(command_res_urb);
error1:
usb_free_urb(command_out_urb);
error0:
usb_put_dev(udev); return retval;
}
staticvoid vub300_disconnect(struct usb_interface *interface)
{ /* NOT irq */ struct vub300_mmc_host *vub300 = usb_get_intfdata(interface); if (!vub300 || !vub300->mmc) { return;
} else { struct mmc_host *mmc = vub300->mmc; if (!vub300->mmc) { return;
} else { int ifnum = interface_to_InterfaceNumber(interface);
usb_set_intfdata(interface, NULL); /* prevent more I/O from starting */
vub300->interface = NULL;
kref_put(&vub300->kref, vub300_delete);
mmc_remove_host(mmc);
pr_info("USB vub300 remote SDIO host controller[%d]" " now disconnected", ifnum); return;
}
}
}
staticint vub300_post_reset(struct usb_interface *intf)
{ /* NOT irq */ struct vub300_mmc_host *vub300 = usb_get_intfdata(intf); /* we are sure no URBs are active - no locking needed */
vub300->errors = -EPIPE;
mutex_unlock(&vub300->cmd_mutex); return 0;
}
MODULE_AUTHOR("Tony Olech <tony.olech@elandigitalsystems.com>");
MODULE_DESCRIPTION("VUB300 USB to SD/MMC/SDIO adapter driver");
MODULE_LICENSE("GPL");
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.26 Sekunden
(vorverarbeitet am 2026-05-01)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.