in core/mspro_block.c [969:1116]
static int mspro_block_read_attributes(struct memstick_dev *card)
{
struct mspro_block_data *msb = memstick_get_drvdata(card);
struct mspro_attribute *attr = NULL;
struct mspro_sys_attr *s_attr = NULL;
unsigned char *buffer = NULL;
int cnt, rc, attr_count;
/* While normally physical device offsets, represented here by
* attr_offset and attr_len will be of large numeric types, we can be
* sure, that attributes are close enough to the beginning of the
* device, to save ourselves some trouble.
*/
unsigned int addr, attr_offset = 0, attr_len = msb->page_size;
attr = kmalloc(msb->page_size, GFP_KERNEL);
if (!attr)
return -ENOMEM;
sg_init_one(&msb->req_sg[0], attr, msb->page_size);
msb->seg_count = 1;
msb->current_seg = 0;
msb->current_page = 0;
msb->data_dir = READ;
msb->transfer_cmd = MSPRO_CMD_READ_ATRB;
msb->setup_transfer(card, attr_offset, attr_len);
memstick_new_req(card->host);
wait_for_completion(&card->mrq_complete);
if (card->current_mrq.error) {
rc = card->current_mrq.error;
goto out_free_attr;
}
if (be16_to_cpu(attr->signature) != MSPRO_BLOCK_SIGNATURE) {
printk(KERN_ERR "%s: unrecognized device signature %x\n",
dev_name(&card->dev), be16_to_cpu(attr->signature));
rc = -ENODEV;
goto out_free_attr;
}
if (attr->count > MSPRO_BLOCK_MAX_ATTRIBUTES) {
printk(KERN_WARNING "%s: way too many attribute entries\n",
dev_name(&card->dev));
attr_count = MSPRO_BLOCK_MAX_ATTRIBUTES;
} else
attr_count = attr->count;
msb->attr_group.attrs = kcalloc(attr_count + 1,
sizeof(*msb->attr_group.attrs),
GFP_KERNEL);
if (!msb->attr_group.attrs) {
rc = -ENOMEM;
goto out_free_attr;
}
msb->attr_group.name = "media_attributes";
buffer = kmemdup(attr, attr_len, GFP_KERNEL);
if (!buffer) {
rc = -ENOMEM;
goto out_free_attr;
}
for (cnt = 0; cnt < attr_count; ++cnt) {
s_attr = kzalloc(sizeof(struct mspro_sys_attr), GFP_KERNEL);
if (!s_attr) {
rc = -ENOMEM;
goto out_free_buffer;
}
msb->attr_group.attrs[cnt] = &s_attr->dev_attr.attr;
addr = be32_to_cpu(attr->entries[cnt].address);
s_attr->size = be32_to_cpu(attr->entries[cnt].size);
dev_dbg(&card->dev, "adding attribute %d: id %x, address %x, "
"size %zx\n", cnt, attr->entries[cnt].id, addr,
s_attr->size);
s_attr->id = attr->entries[cnt].id;
if (mspro_block_attr_name(s_attr->id))
snprintf(s_attr->name, sizeof(s_attr->name), "%s",
mspro_block_attr_name(attr->entries[cnt].id));
else
snprintf(s_attr->name, sizeof(s_attr->name),
"attr_x%02x", attr->entries[cnt].id);
sysfs_attr_init(&s_attr->dev_attr.attr);
s_attr->dev_attr.attr.name = s_attr->name;
s_attr->dev_attr.attr.mode = S_IRUGO;
s_attr->dev_attr.show = mspro_block_attr_show(s_attr->id);
if (!s_attr->size)
continue;
s_attr->data = kmalloc(s_attr->size, GFP_KERNEL);
if (!s_attr->data) {
rc = -ENOMEM;
goto out_free_buffer;
}
if (((addr / msb->page_size) == (attr_offset / msb->page_size))
&& (((addr + s_attr->size - 1) / msb->page_size)
== (attr_offset / msb->page_size))) {
memcpy(s_attr->data, buffer + addr % msb->page_size,
s_attr->size);
continue;
}
attr_offset = (addr / msb->page_size) * msb->page_size;
if ((attr_offset + attr_len) < (addr + s_attr->size)) {
kfree(buffer);
attr_len = (((addr + s_attr->size) / msb->page_size)
+ 1 ) * msb->page_size - attr_offset;
buffer = kmalloc(attr_len, GFP_KERNEL);
if (!buffer) {
rc = -ENOMEM;
goto out_free_attr;
}
}
sg_init_one(&msb->req_sg[0], buffer, attr_len);
msb->seg_count = 1;
msb->current_seg = 0;
msb->current_page = 0;
msb->data_dir = READ;
msb->transfer_cmd = MSPRO_CMD_READ_ATRB;
dev_dbg(&card->dev, "reading attribute range %x, %x\n",
attr_offset, attr_len);
msb->setup_transfer(card, attr_offset, attr_len);
memstick_new_req(card->host);
wait_for_completion(&card->mrq_complete);
if (card->current_mrq.error) {
rc = card->current_mrq.error;
goto out_free_buffer;
}
memcpy(s_attr->data, buffer + addr % msb->page_size,
s_attr->size);
}
rc = 0;
out_free_buffer:
kfree(buffer);
out_free_attr:
kfree(attr);
return rc;
}