mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-07 16:58:04 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
38
drivers/memstick/core/Kconfig
Normal file
38
drivers/memstick/core/Kconfig
Normal file
|
@ -0,0 +1,38 @@
|
|||
#
|
||||
# MemoryStick core configuration
|
||||
#
|
||||
|
||||
comment "MemoryStick drivers"
|
||||
|
||||
config MEMSTICK_UNSAFE_RESUME
|
||||
bool "Allow unsafe resume (DANGEROUS)"
|
||||
help
|
||||
If you say Y here, the MemoryStick layer will assume that all
|
||||
cards stayed in their respective slots during the suspend. The
|
||||
normal behaviour is to remove them at suspend and
|
||||
redetecting them at resume. Breaking this assumption will
|
||||
in most cases result in data corruption.
|
||||
|
||||
This option is usually just for embedded systems which use
|
||||
a MemoryStick card for rootfs. Most people should say N here.
|
||||
|
||||
config MSPRO_BLOCK
|
||||
tristate "MemoryStick Pro block device driver"
|
||||
depends on BLOCK
|
||||
help
|
||||
Say Y here to enable the MemoryStick Pro block device driver
|
||||
support. This provides a block device driver, which you can use
|
||||
to mount the filesystem. Almost everyone wishing MemoryStick
|
||||
support should say Y or M here.
|
||||
|
||||
config MS_BLOCK
|
||||
tristate "MemoryStick Standard device driver"
|
||||
depends on BLOCK
|
||||
help
|
||||
Say Y here to enable the MemoryStick Standard device driver
|
||||
support. This provides a block device driver, which you can use
|
||||
to mount the filesystem.
|
||||
This driver works with old (bulky) MemoryStick and MemoryStick Duo
|
||||
but not PRO. Say Y if you have such card.
|
||||
Driver is new and not yet well tested, thus it can damage your card
|
||||
(even permanently)
|
7
drivers/memstick/core/Makefile
Normal file
7
drivers/memstick/core/Makefile
Normal file
|
@ -0,0 +1,7 @@
|
|||
#
|
||||
# Makefile for the kernel MemoryStick core.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MEMSTICK) += memstick.o
|
||||
obj-$(CONFIG_MS_BLOCK) += ms_block.o
|
||||
obj-$(CONFIG_MSPRO_BLOCK) += mspro_block.o
|
654
drivers/memstick/core/memstick.c
Normal file
654
drivers/memstick/core/memstick.c
Normal file
|
@ -0,0 +1,654 @@
|
|||
/*
|
||||
* 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>
|
||||
#include <linux/module.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); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(name);
|
||||
|
||||
MEMSTICK_ATTR(type, "%02X");
|
||||
MEMSTICK_ATTR(category, "%02X");
|
||||
MEMSTICK_ATTR(class, "%02X");
|
||||
|
||||
static struct attribute *memstick_dev_attrs[] = {
|
||||
&dev_attr_type.attr,
|
||||
&dev_attr_category.attr,
|
||||
&dev_attr_class.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(memstick_dev);
|
||||
|
||||
static struct bus_type memstick_bus_type = {
|
||||
.name = "memstick",
|
||||
.dev_groups = memstick_dev_groups,
|
||||
.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;
|
||||
reinit_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;
|
||||
|
||||
idr_preload(GFP_KERNEL);
|
||||
spin_lock(&memstick_host_lock);
|
||||
|
||||
rc = idr_alloc(&memstick_host_idr, host, 0, 0, GFP_NOWAIT);
|
||||
if (rc >= 0)
|
||||
host->id = rc;
|
||||
|
||||
spin_unlock(&memstick_host_lock);
|
||||
idr_preload_end();
|
||||
if (rc < 0)
|
||||
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");
|
2385
drivers/memstick/core/ms_block.c
Normal file
2385
drivers/memstick/core/ms_block.c
Normal file
File diff suppressed because it is too large
Load diff
290
drivers/memstick/core/ms_block.h
Normal file
290
drivers/memstick/core/ms_block.h
Normal file
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* ms_block.h - Sony MemoryStick (legacy) storage support
|
||||
|
||||
* Copyright (C) 2013 Maxim Levitsky <maximlevitsky@gmail.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.
|
||||
*
|
||||
* Minor portions of the driver are copied from mspro_block.c which is
|
||||
* Copyright (C) 2007 Alex Dubov <oakad@yahoo.com>
|
||||
*
|
||||
* Also ms structures were copied from old broken driver by same author
|
||||
* These probably come from MS spec
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MS_BLOCK_NEW_H
|
||||
#define MS_BLOCK_NEW_H
|
||||
|
||||
#define MS_BLOCK_MAX_SEGS 32
|
||||
#define MS_BLOCK_MAX_PAGES ((2 << 16) - 1)
|
||||
|
||||
#define MS_BLOCK_MAX_BOOT_ADDR 0x000c
|
||||
#define MS_BLOCK_BOOT_ID 0x0001
|
||||
#define MS_BLOCK_INVALID 0xffff
|
||||
#define MS_MAX_ZONES 16
|
||||
#define MS_BLOCKS_IN_ZONE 512
|
||||
|
||||
#define MS_BLOCK_MAP_LINE_SZ 16
|
||||
#define MS_BLOCK_PART_SHIFT 3
|
||||
|
||||
|
||||
#define MEMSTICK_UNCORR_ERROR (MEMSTICK_STATUS1_UCFG | \
|
||||
MEMSTICK_STATUS1_UCEX | MEMSTICK_STATUS1_UCDT)
|
||||
|
||||
#define MEMSTICK_CORR_ERROR (MEMSTICK_STATUS1_FGER | MEMSTICK_STATUS1_EXER | \
|
||||
MEMSTICK_STATUS1_DTER)
|
||||
|
||||
#define MEMSTICK_INT_ERROR (MEMSTICK_INT_CMDNAK | MEMSTICK_INT_ERR)
|
||||
|
||||
#define MEMSTICK_OVERWRITE_FLAG_NORMAL \
|
||||
(MEMSTICK_OVERWRITE_PGST1 | \
|
||||
MEMSTICK_OVERWRITE_PGST0 | \
|
||||
MEMSTICK_OVERWRITE_BKST)
|
||||
|
||||
#define MEMSTICK_OV_PG_NORMAL \
|
||||
(MEMSTICK_OVERWRITE_PGST1 | MEMSTICK_OVERWRITE_PGST0)
|
||||
|
||||
#define MEMSTICK_MANAGMENT_FLAG_NORMAL \
|
||||
(MEMSTICK_MANAGEMENT_SYSFLG | \
|
||||
MEMSTICK_MANAGEMENT_SCMS1 | \
|
||||
MEMSTICK_MANAGEMENT_SCMS0) \
|
||||
|
||||
struct ms_boot_header {
|
||||
unsigned short block_id;
|
||||
unsigned short format_reserved;
|
||||
unsigned char reserved0[184];
|
||||
unsigned char data_entry;
|
||||
unsigned char reserved1[179];
|
||||
} __packed;
|
||||
|
||||
|
||||
struct ms_system_item {
|
||||
unsigned int start_addr;
|
||||
unsigned int data_size;
|
||||
unsigned char data_type_id;
|
||||
unsigned char reserved[3];
|
||||
} __packed;
|
||||
|
||||
struct ms_system_entry {
|
||||
struct ms_system_item disabled_block;
|
||||
struct ms_system_item cis_idi;
|
||||
unsigned char reserved[24];
|
||||
} __packed;
|
||||
|
||||
struct ms_boot_attr_info {
|
||||
unsigned char memorystick_class;
|
||||
unsigned char format_unique_value1;
|
||||
unsigned short block_size;
|
||||
unsigned short number_of_blocks;
|
||||
unsigned short number_of_effective_blocks;
|
||||
unsigned short page_size;
|
||||
unsigned char extra_data_size;
|
||||
unsigned char format_unique_value2;
|
||||
unsigned char assembly_time[8];
|
||||
unsigned char format_unique_value3;
|
||||
unsigned char serial_number[3];
|
||||
unsigned char assembly_manufacturer_code;
|
||||
unsigned char assembly_model_code[3];
|
||||
unsigned short memory_manufacturer_code;
|
||||
unsigned short memory_device_code;
|
||||
unsigned short implemented_capacity;
|
||||
unsigned char format_unique_value4[2];
|
||||
unsigned char vcc;
|
||||
unsigned char vpp;
|
||||
unsigned short controller_number;
|
||||
unsigned short controller_function;
|
||||
unsigned char reserved0[9];
|
||||
unsigned char transfer_supporting;
|
||||
unsigned short format_unique_value5;
|
||||
unsigned char format_type;
|
||||
unsigned char memorystick_application;
|
||||
unsigned char device_type;
|
||||
unsigned char reserved1[22];
|
||||
unsigned char format_uniqure_value6[2];
|
||||
unsigned char reserved2[15];
|
||||
} __packed;
|
||||
|
||||
struct ms_cis_idi {
|
||||
unsigned short general_config;
|
||||
unsigned short logical_cylinders;
|
||||
unsigned short reserved0;
|
||||
unsigned short logical_heads;
|
||||
unsigned short track_size;
|
||||
unsigned short page_size;
|
||||
unsigned short pages_per_track;
|
||||
unsigned short msw;
|
||||
unsigned short lsw;
|
||||
unsigned short reserved1;
|
||||
unsigned char serial_number[20];
|
||||
unsigned short buffer_type;
|
||||
unsigned short buffer_size_increments;
|
||||
unsigned short long_command_ecc;
|
||||
unsigned char firmware_version[28];
|
||||
unsigned char model_name[18];
|
||||
unsigned short reserved2[5];
|
||||
unsigned short pio_mode_number;
|
||||
unsigned short dma_mode_number;
|
||||
unsigned short field_validity;
|
||||
unsigned short current_logical_cylinders;
|
||||
unsigned short current_logical_heads;
|
||||
unsigned short current_pages_per_track;
|
||||
unsigned int current_page_capacity;
|
||||
unsigned short mutiple_page_setting;
|
||||
unsigned int addressable_pages;
|
||||
unsigned short single_word_dma;
|
||||
unsigned short multi_word_dma;
|
||||
unsigned char reserved3[128];
|
||||
} __packed;
|
||||
|
||||
|
||||
struct ms_boot_page {
|
||||
struct ms_boot_header header;
|
||||
struct ms_system_entry entry;
|
||||
struct ms_boot_attr_info attr;
|
||||
} __packed;
|
||||
|
||||
struct msb_data {
|
||||
unsigned int usage_count;
|
||||
struct memstick_dev *card;
|
||||
struct gendisk *disk;
|
||||
struct request_queue *queue;
|
||||
spinlock_t q_lock;
|
||||
struct hd_geometry geometry;
|
||||
struct attribute_group attr_group;
|
||||
struct request *req;
|
||||
int caps;
|
||||
int disk_id;
|
||||
|
||||
/* IO */
|
||||
struct workqueue_struct *io_queue;
|
||||
bool io_queue_stopped;
|
||||
struct work_struct io_work;
|
||||
bool card_dead;
|
||||
|
||||
/* Media properties */
|
||||
struct ms_boot_page *boot_page;
|
||||
u16 boot_block_locations[2];
|
||||
int boot_block_count;
|
||||
|
||||
bool read_only;
|
||||
unsigned short page_size;
|
||||
int block_size;
|
||||
int pages_in_block;
|
||||
int zone_count;
|
||||
int block_count;
|
||||
int logical_block_count;
|
||||
|
||||
/* FTL tables */
|
||||
unsigned long *used_blocks_bitmap;
|
||||
unsigned long *erased_blocks_bitmap;
|
||||
u16 *lba_to_pba_table;
|
||||
int free_block_count[MS_MAX_ZONES];
|
||||
bool ftl_initialized;
|
||||
|
||||
/* Cache */
|
||||
unsigned char *cache;
|
||||
unsigned long valid_cache_bitmap;
|
||||
int cache_block_lba;
|
||||
bool need_flush_cache;
|
||||
struct timer_list cache_flush_timer;
|
||||
|
||||
/* Preallocated buffers */
|
||||
unsigned char *block_buffer;
|
||||
struct scatterlist prealloc_sg[MS_BLOCK_MAX_SEGS+1];
|
||||
|
||||
|
||||
/* handler's local data */
|
||||
struct ms_register_addr reg_addr;
|
||||
bool addr_valid;
|
||||
|
||||
u8 command_value;
|
||||
bool command_need_oob;
|
||||
struct scatterlist *current_sg;
|
||||
int current_sg_offset;
|
||||
|
||||
struct ms_register regs;
|
||||
int current_page;
|
||||
|
||||
int state;
|
||||
int exit_error;
|
||||
bool int_polling;
|
||||
unsigned long int_timeout;
|
||||
|
||||
};
|
||||
|
||||
enum msb_readpage_states {
|
||||
MSB_RP_SEND_BLOCK_ADDRESS = 0,
|
||||
MSB_RP_SEND_READ_COMMAND,
|
||||
|
||||
MSB_RP_SEND_INT_REQ,
|
||||
MSB_RP_RECEIVE_INT_REQ_RESULT,
|
||||
|
||||
MSB_RP_SEND_READ_STATUS_REG,
|
||||
MSB_RP_RECEIVE_STATUS_REG,
|
||||
|
||||
MSB_RP_SEND_OOB_READ,
|
||||
MSB_RP_RECEIVE_OOB_READ,
|
||||
|
||||
MSB_RP_SEND_READ_DATA,
|
||||
MSB_RP_RECEIVE_READ_DATA,
|
||||
};
|
||||
|
||||
enum msb_write_block_states {
|
||||
MSB_WB_SEND_WRITE_PARAMS = 0,
|
||||
MSB_WB_SEND_WRITE_OOB,
|
||||
MSB_WB_SEND_WRITE_COMMAND,
|
||||
|
||||
MSB_WB_SEND_INT_REQ,
|
||||
MSB_WB_RECEIVE_INT_REQ,
|
||||
|
||||
MSB_WB_SEND_WRITE_DATA,
|
||||
MSB_WB_RECEIVE_WRITE_CONFIRMATION,
|
||||
};
|
||||
|
||||
enum msb_send_command_states {
|
||||
MSB_SC_SEND_WRITE_PARAMS,
|
||||
MSB_SC_SEND_WRITE_OOB,
|
||||
MSB_SC_SEND_COMMAND,
|
||||
|
||||
MSB_SC_SEND_INT_REQ,
|
||||
MSB_SC_RECEIVE_INT_REQ,
|
||||
|
||||
};
|
||||
|
||||
enum msb_reset_states {
|
||||
MSB_RS_SEND,
|
||||
MSB_RS_CONFIRM,
|
||||
};
|
||||
|
||||
enum msb_par_switch_states {
|
||||
MSB_PS_SEND_SWITCH_COMMAND,
|
||||
MSB_PS_SWICH_HOST,
|
||||
MSB_PS_CONFIRM,
|
||||
};
|
||||
|
||||
struct chs_entry {
|
||||
unsigned long size;
|
||||
unsigned char sec;
|
||||
unsigned short cyl;
|
||||
unsigned char head;
|
||||
};
|
||||
|
||||
static int msb_reset(struct msb_data *msb, bool full);
|
||||
|
||||
static int h_msb_default_bad(struct memstick_dev *card,
|
||||
struct memstick_request **mrq);
|
||||
|
||||
#define __dbg(level, format, ...) \
|
||||
do { \
|
||||
if (debug >= level) \
|
||||
pr_err(format "\n", ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define dbg(format, ...) __dbg(1, format, ## __VA_ARGS__)
|
||||
#define dbg_verbose(format, ...) __dbg(2, format, ## __VA_ARGS__)
|
||||
|
||||
#endif
|
1488
drivers/memstick/core/mspro_block.c
Normal file
1488
drivers/memstick/core/mspro_block.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue