summaryrefslogtreecommitdiffstats
path: root/drivers/ieee1394/csr1212.c
AgeCommit message (Expand)Author
2009-06-15ieee1394/csr1212: fix false positive kmemcheck reportVegard Nossum
2009-01-04ieee1394: ignore nonzero Bus_Info_Block.max_rom, fetch config ROM in quadletsStefan Richter
2008-10-15ieee1394: Use DIV_ROUND_UPJulia Lawall
2008-07-14ieee1394: reduce log noise about config ROM CRC errorsStefan Richter
2007-10-16ieee1394: csr1212: proper refcountingStefan Richter
2007-04-30ieee1394: csr1212: log if devices have CRC errors in their ROMStefan Richter
2007-04-30ieee1394: csr1212: more sensible names for jump targetsStefan Richter
2007-04-30ieee1394: csr1212: warn on unreachable codeStefan Richter
2007-04-30ieee1394: shrink csr1212_new_string_descriptor_leafStefan Richter
2007-04-30ieee1394: csr1212: coding styleStefan Richter
2007-04-30ieee1394: replace vmalloc by kmalloc in csr1212Stefan Richter
2007-04-30ieee1394: de-inline some functionsStefan Richter
2007-04-30ieee1394: stricter error checks in csr1212Stefan Richter
2007-04-30ieee1394: csr1212: rename some typesStefan Richter
2007-04-30ieee1394: drop csr1212's support for external compilationStefan Richter
2007-04-30ieee1394: remove unused csr1212 codeStefan Richter
2007-02-08ieee1394: nodemgr: check info_length in ROM header earlierStefan Richter
2007-02-08ieee1394: modified csr1212_key_id_type_map to support lisightAndrea Guzzo
2006-06-12ieee1394: add preprocessor constant for invalid csr addressBen Collins
2005-11-22csr1212: add check for !validJody McIntyre
2005-11-22csr1212: check results of keyval readsJody McIntyre
2005-11-07kmalloc/kzalloc changes:Stefan Richter
2005-07-10[PATCH] Sync up ieee-1394Ben Collins
2005-04-16Linux-2.6.12-rc2Linus Torvalds
n192' href='#n192'>192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
/*
 * drivers/usb/core/endpoint.c
 *
 * (C) Copyright 2002,2004,2006 Greg Kroah-Hartman
 * (C) Copyright 2002,2004 IBM Corp.
 * (C) Copyright 2006 Novell Inc.
 *
 * Endpoint sysfs stuff
 *
 */

#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/idr.h>
#include <linux/usb.h>
#include "usb.h"

#define MAX_ENDPOINT_MINORS (64*128*32)
static int usb_endpoint_major;
static DEFINE_IDR(endpoint_idr);

struct ep_device {
	struct usb_endpoint_descriptor *desc;
	struct usb_device *udev;
	struct device dev;
	int minor;
};
#define to_ep_device(_dev) \
	container_of(_dev, struct ep_device, dev)

struct ep_attribute {
	struct attribute attr;
	ssize_t (*show)(struct usb_device *,
			struct usb_endpoint_descriptor *, char *);
};
#define to_ep_attribute(_attr) \
	container_of(_attr, struct ep_attribute, attr)

#define usb_ep_attr(field, format_string)			\
static ssize_t show_ep_##field(struct device *dev,		\
			       struct device_attribute *attr,	\
			       char *buf)			\
{								\
	struct ep_device *ep = to_ep_device(dev);		\
	return sprintf(buf, format_string, ep->desc->field);	\
}								\
static DEVICE_ATTR(field, S_IRUGO, show_ep_##field, NULL);

usb_ep_attr(bLength, "%02x\n")
usb_ep_attr(bEndpointAddress, "%02x\n")
usb_ep_attr(bmAttributes, "%02x\n")
usb_ep_attr(bInterval, "%02x\n")

static ssize_t show_ep_wMaxPacketSize(struct device *dev,
				      struct device_attribute *attr, char *buf)
{
	struct ep_device *ep = to_ep_device(dev);
	return sprintf(buf, "%04x\n",
			le16_to_cpu(ep->desc->wMaxPacketSize) & 0x07ff);
}
static DEVICE_ATTR(wMaxPacketSize, S_IRUGO, show_ep_wMaxPacketSize, NULL);

static ssize_t show_ep_type(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	struct ep_device *ep = to_ep_device(dev);
	char *type = "unknown";

	switch (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
	case USB_ENDPOINT_XFER_CONTROL:
		type = "Control";
		break;
	case USB_ENDPOINT_XFER_ISOC:
		type = "Isoc";
		break;
	case USB_ENDPOINT_XFER_BULK:
		type = "Bulk";
		break;
	case USB_ENDPOINT_XFER_INT:
		type = "Interrupt";
		break;
	}
	return sprintf(buf, "%s\n", type);
}
static DEVICE_ATTR(type, S_IRUGO, show_ep_type, NULL);

static ssize_t show_ep_interval(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct ep_device *ep = to_ep_device(dev);
	char unit;
	unsigned interval = 0;
	unsigned in;

	in = (ep->desc->bEndpointAddress & USB_DIR_IN);

	switch (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
	case USB_ENDPOINT_XFER_CONTROL:
		if (ep->udev->speed == USB_SPEED_HIGH) 	/* uframes per NAK */
			interval = ep->desc->bInterval;
		break;
	case USB_ENDPOINT_XFER_ISOC:
		interval = 1 << (ep->desc->bInterval - 1);
		break;
	case USB_ENDPOINT_XFER_BULK:
		if (ep->udev->speed == USB_SPEED_HIGH && !in) /* uframes per NAK */
			interval = ep->desc->bInterval;
		break;
	case USB_ENDPOINT_XFER_INT:
		if (ep->udev->speed == USB_SPEED_HIGH)
			interval = 1 << (ep->desc->bInterval - 1);
		else
			interval = ep->desc->bInterval;
		break;
	}
	interval *= (ep->udev->speed == USB_SPEED_HIGH) ? 125 : 1000;
	if (interval % 1000)
		unit = 'u';
	else {
		unit = 'm';
		interval /= 1000;
	}

	return sprintf(buf, "%d%cs\n", interval, unit);
}
static DEVICE_ATTR(interval, S_IRUGO, show_ep_interval, NULL);

static ssize_t show_ep_direction(struct device *dev,
				 struct device_attribute *attr, char *buf)
{
	struct ep_device *ep = to_ep_device(dev);
	char *direction;

	if ((ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
			USB_ENDPOINT_XFER_CONTROL)
		direction = "both";
	else if (ep->desc->bEndpointAddress & USB_DIR_IN)
		direction = "in";
	else
		direction = "out";
	return sprintf(buf, "%s\n", direction);
}
static DEVICE_ATTR(direction, S_IRUGO, show_ep_direction, NULL);

static struct attribute *ep_dev_attrs[] = {
	&dev_attr_bLength.attr,
	&dev_attr_bEndpointAddress.attr,
	&dev_attr_bmAttributes.attr,
	&dev_attr_bInterval.attr,
	&dev_attr_wMaxPacketSize.attr,
	&dev_attr_interval.attr,
	&dev_attr_type.attr,
	&dev_attr_direction.attr,
	NULL,
};
static struct attribute_group ep_dev_attr_grp = {
	.attrs = ep_dev_attrs,
};
static struct attribute_group *ep_dev_groups[] = {
	&ep_dev_attr_grp,
	NULL
};

static int usb_endpoint_major_init(void)
{
	dev_t dev;
	int error;

	error = alloc_chrdev_region(&dev, 0, MAX_ENDPOINT_MINORS,
				    "usb_endpoint");
	if (error) {
		err("unable to get a dynamic major for usb endpoints");
		return error;
	}
	usb_endpoint_major = MAJOR(dev);

	return error;
}

static void usb_endpoint_major_cleanup(void)
{
	unregister_chrdev_region(MKDEV(usb_endpoint_major, 0),
				 MAX_ENDPOINT_MINORS);
}

static int endpoint_get_minor(struct ep_device *ep_dev)
{
	static DEFINE_MUTEX(minor_lock);
	int retval = -ENOMEM;
	int id;

	mutex_lock(&minor_lock);
	if (idr_pre_get(&endpoint_idr, GFP_KERNEL) == 0)
		goto exit;

	retval = idr_get_new(&endpoint_idr, ep_dev, &id);
	if (retval < 0) {
		if (retval == -EAGAIN)
			retval = -ENOMEM;
		goto exit;
	}
	ep_dev->minor = id & MAX_ID_MASK;
exit:
	mutex_unlock(&minor_lock);
	return retval;
}

static void endpoint_free_minor(struct ep_device *ep_dev)
{
	idr_remove(&endpoint_idr, ep_dev->minor);
}

static struct endpoint_class {
	struct kref kref;
	struct class *class;
} *ep_class;

static int init_endpoint_class(void)
{
	int result = 0;

	if (ep_class != NULL) {
		kref_get(&ep_class->kref);
		goto exit;
	}

	ep_class = kmalloc(sizeof(*ep_class), GFP_KERNEL);
	if (!ep_class) {
		result = -ENOMEM;
		goto exit;
	}

	kref_init(&ep_class->kref);
	ep_class->class = class_create(THIS_MODULE, "usb_endpoint");
	if (IS_ERR(ep_class->class)) {
		result = PTR_ERR(ep_class->class);
		goto class_create_error;
	}

	result = usb_endpoint_major_init();
	if (result)
		goto endpoint_major_error;

	goto exit;

endpoint_major_error:
	class_destroy(ep_class->class);
class_create_error:
	kfree(ep_class);
	ep_class = NULL;
exit:
	return result;
}

static void release_endpoint_class(struct kref *kref)
{
	/* Ok, we cheat as we know we only have one ep_class */
	class_destroy(ep_class->class);
	kfree(ep_class);
	ep_class = NULL;
	usb_endpoint_major_cleanup();
}

static void destroy_endpoint_class(void)
{
	if (ep_class)
		kref_put(&ep_class->kref, release_endpoint_class);
}

static void ep_device_release(struct device *dev)
{
	struct ep_device *ep_dev = to_ep_device(dev);

	endpoint_free_minor(ep_dev);
	kfree(ep_dev);
}

int usb_create_ep_files(struct device *parent,
			struct usb_host_endpoint *endpoint,
			struct usb_device *udev)
{
	char name[8];
	struct ep_device *ep_dev;
	int retval;

	retval