static int mspro_block_read_attributes()

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;
}