b1538bcf75
This way those tuples that the core cares about are consumed by the core code, and tuples that only function drivers might make sense of are available to drivers. Signed-off-by: Nicolas Pitre <npitre@mvista.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
145 lines
3.1 KiB
C
145 lines
3.1 KiB
C
/*
|
|
* linux/drivers/mmc/core/sdio_cis.c
|
|
*
|
|
* Author: Nicolas Pitre
|
|
* Created: June 11, 2007
|
|
* Copyright: MontaVista Software Inc.
|
|
*
|
|
* Copyright 2007 Pierre Ossman
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or (at
|
|
* your option) any later version.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/mmc/host.h>
|
|
#include <linux/mmc/sdio.h>
|
|
#include <linux/mmc/sdio_func.h>
|
|
|
|
#include "sdio_cis.h"
|
|
#include "sdio_ops.h"
|
|
|
|
static int cistpl_manfid(struct sdio_func *func,
|
|
const unsigned char *buf,
|
|
unsigned size)
|
|
{
|
|
/* TPLMID_MANF */
|
|
func->vendor = buf[0] | (buf[1] << 8);
|
|
|
|
/* TPLMID_CARD */
|
|
func->device = buf[2] | (buf[3] << 8);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct cis_tpl {
|
|
unsigned char code;
|
|
unsigned char min_size;
|
|
int (*parse)(struct sdio_func *, const unsigned char *buf, unsigned size);
|
|
};
|
|
|
|
static const struct cis_tpl cis_tpl_list[] = {
|
|
{ 0x15, 3, /* cistpl_vers_1 */ },
|
|
{ 0x20, 4, cistpl_manfid },
|
|
{ 0x21, 2, /* cistpl_funcid */ },
|
|
{ 0x22, 0, /* cistpl_funce */ },
|
|
};
|
|
|
|
int sdio_read_cis(struct sdio_func *func)
|
|
{
|
|
int ret;
|
|
struct sdio_func_tuple *this, **prev;
|
|
unsigned i, ptr = 0;
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
unsigned char x;
|
|
ret = mmc_io_rw_direct(func->card, 0, 0,
|
|
func->num * 0x100 + SDIO_FBR_CIS + i, 0, &x);
|
|
if (ret)
|
|
return ret;
|
|
ptr |= x << (i * 8);
|
|
}
|
|
|
|
/* find the list tail */
|
|
for (prev = &func->tuples; *prev; prev = &(*prev)->next);
|
|
|
|
do {
|
|
unsigned char tpl_code, tpl_link;
|
|
|
|
ret = mmc_io_rw_direct(func->card, 0, 0, ptr++, 0, &tpl_code);
|
|
if (ret)
|
|
break;
|
|
|
|
/* 0xff means we're done */
|
|
if (tpl_code == 0xff)
|
|
break;
|
|
|
|
ret = mmc_io_rw_direct(func->card, 0, 0, ptr++, 0, &tpl_link);
|
|
if (ret)
|
|
break;
|
|
|
|
this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
|
|
if (!this)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < tpl_link; i++) {
|
|
ret = mmc_io_rw_direct(func->card, 0, 0,
|
|
ptr + i, 0, &this->data[i]);
|
|
if (ret)
|
|
break;
|
|
}
|
|
if (ret) {
|
|
kfree(this);
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
|
|
if (cis_tpl_list[i].code == tpl_code)
|
|
break;
|
|
if (i >= ARRAY_SIZE(cis_tpl_list)) {
|
|
/* this tuple is unknown to the core */
|
|
this->next = NULL;
|
|
this->code = tpl_code;
|
|
this->size = tpl_link;
|
|
*prev = this;
|
|
prev = &this->next;
|
|
printk(KERN_DEBUG
|
|
"%s: queuing CIS tuple 0x%02x length %u\n",
|
|
sdio_func_id(func), tpl_code, tpl_link);
|
|
} else {
|
|
const struct cis_tpl *tpl = cis_tpl_list + i;
|
|
if (tpl_link < tpl->min_size) {
|
|
printk(KERN_ERR
|
|
"%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n",
|
|
sdio_func_id(func), tpl_code, tpl_link, tpl->min_size);
|
|
ret = -EINVAL;
|
|
} else if (tpl->parse)
|
|
ret = tpl->parse(func, this->data, tpl_link);
|
|
kfree(this);
|
|
}
|
|
|
|
ptr += tpl_link;
|
|
} while (!ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void sdio_free_cis(struct sdio_func *func)
|
|
{
|
|
struct sdio_func_tuple *tuple, *victim;
|
|
|
|
tuple = func->tuples;
|
|
|
|
while (tuple) {
|
|
victim = tuple;
|
|
tuple = tuple->next;
|
|
kfree(victim);
|
|
}
|
|
|
|
func->tuples = NULL;
|
|
}
|
|
|