/* * Sony MemoryStick support * * Copyright (C) 2007 Alex Dubov <oakad@yahoo.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Special thanks to Carlos Corbacho for providing various MemoryStick cards * that made this driver possible. * */ #include <linux/memstick.h> #include <linux/idr.h> #include <linux/fs.h> #include <linux/delay.h> #include <linux/slab.h> #define DRIVER_NAME "memstick" static unsigned int cmd_retries = 3; module_param(cmd_retries, uint, 0644); static struct workqueue_struct *workqueue; static DEFINE_IDR(memstick_host_idr); static DEFINE_SPINLOCK(memstick_host_lock); static int memstick_dev_match(struct memstick_dev *card, struct memstick_device_id *id) { if (id->match_flags & MEMSTICK_MATCH_ALL) { if ((id->type == card->id.type) && (id->category == card->id.category) && (id->class == card->id.class)) return 1; } return 0; } static int memstick_bus_match(struct device *dev, struct device_driver *drv) { struct memstick_dev *card = container_of(dev, struct memstick_dev, dev); struct memstick_driver *ms_drv = container_of(drv, struct memstick_driver, driver); struct memstick_device_id *ids = ms_drv->id_table; if (ids) { while (ids->match_flags) { if (memstick_dev_match(card, ids)) return 1; ++ids; } } return 0; } static int memstick_uevent(struct device *dev, struct kobj_uevent_env *env) { struct memstick_dev *card = container_of(dev, struct memstick_dev, dev); if (add_uevent_var(env, "MEMSTICK_TYPE=%02X", card->id.type)) return -ENOMEM; if (add_uevent_var(env, "MEMSTICK_CATEGORY=%02X", card->id.category)) return -ENOMEM; if (add_uevent_var(env, "MEMSTICK_CLASS=%02X", card->id.class)) return -ENOMEM; return 0; } static int memstick_device_probe(struct device *dev) { struct memstick_dev *card = container_of(dev, struct memstick_dev, dev); struct memstick_driver *drv = container_of(dev->driver, struct memstick_driver, driver); int rc = -ENODEV; if (dev->driver && drv->probe) { rc = drv->probe(card); if (!rc) get_device(dev); } return rc; } static int memstick_device_remove(struct device *dev) { struct memstick_dev *card = container_of(dev, struct memstick_dev, dev); struct memstick_driver *drv = container_of(dev->driver, struct memstick_driver, driver); if (dev->driver && drv->remove) { drv->remove(card); card->dev.driver = NULL; } put_device(dev); return 0; } #ifdef CONFIG_PM static int memstick_device_suspend(struct device *dev, pm_message_t state) { struct memstick_dev *card = container_of(dev, struct memstick_dev, dev); struct memstick_driver *drv = container_of(dev->driver, struct memstick_driver, driver); if (dev->driver && drv->suspend) return drv->suspend(card, state); return 0; } static int memstick_device_resume(struct device *dev) { struct memstick_dev *card = container_of(dev, struct memstick_dev, dev); struct memstick_driver *drv = container_of(dev->driver, struct memstick_driver, driver); if (dev->driver && drv->resume) return drv->resume(card); return 0; } #else #define memstick_device_suspend NULL #define memstick_device_resume NULL #endif /* CONFIG_PM */ #define MEMSTICK_ATTR(name, format) \ static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ struct memstick_dev *card = container_of(dev, struct memstick_dev, \ dev); \ return sprintf(buf, format, card->id.name); \ } MEMSTICK_ATTR(type, "%02X"); MEMSTICK_ATTR(category, "%02X"); MEMSTICK_ATTR(class, "%02X"); #define MEMSTICK_ATTR_RO(name) __ATTR(name, S_IRUGO, name##_show, NULL) static struct device_attribute memstick_dev_attrs[] = { MEMSTICK_ATTR_RO(type), MEMSTICK_ATTR_RO(category), MEMSTICK_ATTR_RO(class), __ATTR_NULL }; static struct bus_type memstick_bus_type = { .name = "memstick", .dev_attrs = memstick_dev_attrs, .match = memstick_bus_match, .uevent = memstick_uevent, .probe = memstick_device_probe, .remove = memstick_device_remove, .suspend = memstick_device_suspend, .resume = memstick_device_resume }; static void memstick_free(struct device *dev) { struct memstick_host *host = container_of(dev, struct memstick_host, dev); kfree(host); } static struct class memstick_host_class = { .name = "memstick_host", .dev_release = memstick_free }; static void memstick_free_card(struct device *dev) { struct memstick_dev *card = container_of(dev, struct memstick_dev, dev); kfree(card); } static int memstick_dummy_check(struct memstick_dev *card) { return 0; } /** * memstick_detect_change - schedule media detection on memstick host * @host - host to use */ void memstick_detect_change(struct memstick_host *host) { queue_work(workqueue, &host->media_checker); } EXPORT_SYMBOL(memstick_detect_change); /** * memstick_next_req - called by host driver to obtain next request to process * @host - host to use * @mrq - pointer to stick the request to * * Host calls this function from idle state (*mrq == NULL) or after finishing * previous request (*mrq should point to it). If previous request was * unsuccessful, it is retried for predetermined number of times. Return value * of 0 means that new request was assigned to the host. */ int memstick_next_req(struct memstick_host *host, struct memstick_request **mrq) { int rc = -ENXIO; if ((*mrq) && (*mrq)->error && host->retries) { (*mrq)->error = rc; host->retries--; return 0; } if (host->card && host->card->next_request) rc = host->card->next_request(host->card, mrq); if (!rc) host->retries = cmd_retries > 1 ? cmd_retries - 1 : 1; else *mrq = NULL; return rc; } EXPORT_SYMBOL(memstick_next_req); /** * memstick_new_req - notify the host that some requests are pending * @host - host to use */ void memstick_new_req(struct memstick_host *host) { if (host->card) { host->retries = cmd_retries; INIT_COMPLETION(host->card->mrq_complete); host->request(host); } } EXPORT_SYMBOL(memstick_new_req); /** * memstick_init_req_sg - set request fields needed for bulk data transfer * @mrq - request to use * @tpc - memstick Transport Protocol Command * @sg - TPC argument */ void memstick_init_req_sg(struct memstick_request *mrq, unsigned char tpc, const struct scatterlist *sg) { mrq->tpc = tpc; if (tpc & 8) mrq->data_dir = WRITE; else mrq->data_dir = READ; mrq->sg = *sg; mrq->long_data = 1; if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD) mrq->need_card_int = 1; else mrq->need_card_int = 0; } EXPORT_SYMBOL(memstick_init_req_sg); /** * memstick_init_req - set request fields needed for short data transfer * @mrq - request to use * @tpc - memstick Transport Protocol Command * @buf - TPC argument buffer * @length - TPC argument size * * The intended use of this function (transfer of data items several bytes * in size) allows us to just copy the value between request structure and * user supplied buffer. */ void memstick_init_req(struct memstick_request *mrq, unsigned char tpc, const void *buf, size_t length) { mrq->tpc = tpc; if (tpc & 8) mrq->data_dir = WRITE; else mrq->data_dir = READ; mrq->data_len = length > sizeof(mrq->data) ? sizeof(mrq->data) : length; if (mrq->data_dir == WRITE) memcpy(mrq->data, buf, mrq->data_len); mrq->long_data = 0; if (tpc == MS_TPC_SET_CMD || tpc == MS_TPC_EX_SET_CMD) mrq->need_card_int = 1; else mrq->need_card_int = 0; } EXPORT_SYMBOL(memstick_init_req); /* * Functions prefixed with "h_" are protocol callbacks. They can be called from * interrupt context. Return value of 0 means that request processing is still * ongoing, while special error value of -EAGAIN means that current request is * finished (and request processor should come back some time later). */ static int h_memstick_read_dev_id(struct memstick_dev *card, struct memstick_request **mrq) { struct ms_id_register id_reg; if (!(*mrq)) { memstick_init_req(&card->current_mrq, MS_TPC_READ_REG, NULL, sizeof(struct ms_id_register)); *mrq = &card->current_mrq; return 0; } else { if (!(*mrq)->error) { memcpy(&id_reg, (*mrq)->data, sizeof(id_reg)); card->id.match_flags = MEMSTICK_MATCH_ALL; card->id.type = id_reg.type; card->id.category = id_reg.category; card->id.class = id_reg.class; dev_dbg(&card->dev, "if_mode = %02x\n", id_reg.if_mode); } complete(&card->mrq_complete); return -EAGAIN; } } static int h_memstick_set_rw_addr(struct memstick_dev *card, struct memstick_request **mrq) { if (!(*mrq)) { memstick_init_req(&card->current_mrq, MS_TPC_SET_RW_REG_ADRS, (char *)&card->reg_addr, sizeof(card->reg_addr)); *mrq = &card->current_mrq; return 0; } else { complete(&card->mrq_complete); return -EAGAIN; } } /** * memstick_set_rw_addr - issue SET_RW_REG_ADDR request and wait for it to * complete * @card - media device to use */ int memstick_set_rw_addr(struct memstick_dev *card) { card->next_request = h_memstick_set_rw_addr; memstick_new_req(card->host); wait_for_completion(&card->mrq_complete); return card->current_mrq.error; } EXPORT_SYMBOL(memstick_set_rw_addr); static struct memstick_dev *memstick_alloc_card(struct memstick_host *host) { struct memstick_dev *card = kzalloc(sizeof(struct memstick_dev), GFP_KERNEL); struct memstick_dev *old_card = host->card; struct ms_id_register id_reg; if (card) { card->host = host; dev_set_name(&card->dev, "%s", dev_name(&host->dev)); card->dev.parent = &host->dev; card->dev.bus = &memstick_bus_type; card->dev.release = memstick_free_card; card->check = memstick_dummy_check; card->reg_addr.r_offset = offsetof(struct ms_register, id); card->reg_addr.r_length = sizeof(id_reg); card->reg_addr.w_offset = offsetof(struct ms_register, id); card->reg_addr.w_length = sizeof(id_reg); init_completion(&card->mrq_complete); host->card = card; if (memstick_set_rw_addr(card)) goto err_out; card->next_request = h_memstick_read_dev_id; memstick_new_req(host); wait_for_completion(&card->mrq_complete); if (card->current_mrq.error) goto err_out; } host->card = old_card; return card; err_out: host->card = old_card; kfree(card); return NULL; } static int memstick_power_on(struct memstick_host *host) { int rc = host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_ON); if (!rc) rc = host->set_param(host, MEMSTICK_INTERFACE, MEMSTICK_SERIAL); return rc; } static void memstick_check(struct work_struct *work) { struct memstick_host *host = container_of(work, struct memstick_host, media_checker); struct memstick_dev *card; dev_dbg(&host->dev, "memstick_check started\n"); mutex_lock(&host->lock); if (!host->card) { if (memstick_power_on(host)) goto out_power_off; } else if (host->card->stop) host->card->stop(host->card); card = memstick_alloc_card(host); if (!card) { if (host->card) { device_unregister(&host->card->dev); host->card = NULL; } } else { dev_dbg(&host->dev, "new card %02x, %02x, %02x\n", card->id.type, card->id.category, card->id.class); if (host->card) { if (memstick_set_rw_addr(host->card) || !memstick_dev_match(host->card, &card->id) || !(host->card->check(host->card))) { device_unregister(&host->card->dev); host->card = NULL; } else if (host->card->start) host->card->start(host->card); } if (!host->card) { host->card = card; if (device_register(&card->dev)) { put_device(&card->dev); kfree(host->card); host->card = NULL; } } else kfree(card); } out_power_off: if (!host->card) host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); mutex_unlock(&host->lock); dev_dbg(&host->dev, "memstick_check finished\n"); } /** * memstick_alloc_host - allocate a memstick_host structure * @extra: size of the user private data to allocate * @dev: parent device of the host */ struct memstick_host *memstick_alloc_host(unsigned int extra, struct device *dev) { struct memstick_host *host; host = kzalloc(sizeof(struct memstick_host) + extra, GFP_KERNEL); if (host) { mutex_init(&host->lock); INIT_WORK(&host->media_checker, memstick_check); host->dev.class = &memstick_host_class; host->dev.parent = dev; device_initialize(&host->dev); } return host; } EXPORT_SYMBOL(memstick_alloc_host); /** * memstick_add_host - start request processing on memstick host * @host - host to use */ int memstick_add_host(struct memstick_host *host) { int rc; while (1) { if (!idr_pre_get(&memstick_host_idr, GFP_KERNEL)) return -ENOMEM; spin_lock(&memstick_host_lock); rc = idr_get_new(&memstick_host_idr, host, &host->id); spin_unlock(&memstick_host_lock); if (!rc) break; else if (rc != -EAGAIN) return rc; } dev_set_name(&host->dev, "memstick%u", host->id); rc = device_add(&host->dev); if (rc) { spin_lock(&memstick_host_lock); idr_remove(&memstick_host_idr, host->id); spin_unlock(&memstick_host_lock); return rc; } host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); memstick_detect_change(host); return 0; } EXPORT_SYMBOL(memstick_add_host); /** * memstick_remove_host - stop request processing on memstick host * @host - host to use */ void memstick_remove_host(struct memstick_host *host) { flush_workqueue(workqueue); mutex_lock(&host->lock); if (host->card) device_unregister(&host->card->dev); host->card = NULL; host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); mutex_unlock(&host->lock); spin_lock(&memstick_host_lock); idr_remove(&memstick_host_idr, host->id); spin_unlock(&memstick_host_lock); device_del(&host->dev); } EXPORT_SYMBOL(memstick_remove_host); /** * memstick_free_host - free memstick host * @host - host to use */ void memstick_free_host(struct memstick_host *host) { mutex_destroy(&host->lock); put_device(&host->dev); } EXPORT_SYMBOL(memstick_free_host); /** * memstick_suspend_host - notify bus driver of host suspension * @host - host to use */ void memstick_suspend_host(struct memstick_host *host) { mutex_lock(&host->lock); host->set_param(host, MEMSTICK_POWER, MEMSTICK_POWER_OFF); mutex_unlock(&host->lock); } EXPORT_SYMBOL(memstick_suspend_host); /** * memstick_resume_host - notify bus driver of host resumption * @host - host to use */ void memstick_resume_host(struct memstick_host *host) { int rc = 0; mutex_lock(&host->lock); if (host->card) rc = memstick_power_on(host); mutex_unlock(&host->lock); if (!rc) memstick_detect_change(host); } EXPORT_SYMBOL(memstick_resume_host); int memstick_register_driver(struct memstick_driver *drv) { drv->driver.bus = &memstick_bus_type; return driver_register(&drv->driver); } EXPORT_SYMBOL(memstick_register_driver); void memstick_unregister_driver(struct memstick_driver *drv) { driver_unregister(&drv->driver); } EXPORT_SYMBOL(memstick_unregister_driver); static int __init memstick_init(void) { int rc; workqueue = create_freezable_workqueue("kmemstick"); if (!workqueue) return -ENOMEM; rc = bus_register(&memstick_bus_type); if (!rc) rc = class_register(&memstick_host_class); if (!rc) return 0; bus_unregister(&memstick_bus_type); destroy_workqueue(workqueue); return rc; } static void __exit memstick_exit(void) { class_unregister(&memstick_host_class); bus_unregister(&memstick_bus_type); destroy_workqueue(workqueue); idr_destroy(&memstick_host_idr); } module_init(memstick_init); module_exit(memstick_exit); MODULE_AUTHOR("Alex Dubov"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Sony MemoryStick core driver");