| Michal Nazarewicz | c6c5600 | 2010-05-05 12:53:15 +0200 | [diff] [blame] | 1 | #include <linux/module.h> | 
 | 2 | #include <linux/utsname.h> | 
 | 3 |  | 
 | 4 |  | 
 | 5 | /* | 
 | 6 |  * kbuild is not very cooperative with respect to linking separately | 
 | 7 |  * compiled library objects into one module.  So for now we won't use | 
 | 8 |  * separate compilation ... ensuring init/exit sections work to shrink | 
 | 9 |  * the runtime footprint, and giving us at least some parts of what | 
 | 10 |  * a "gcc --combine ... part1.c part2.c part3.c ... " build would. | 
 | 11 |  */ | 
 | 12 |  | 
 | 13 | #include "composite.c" | 
 | 14 | #include "usbstring.c" | 
 | 15 | #include "config.c" | 
 | 16 | #include "epautoconf.c" | 
 | 17 |  | 
 | 18 | #if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS | 
 | 19 | #  if defined USB_ETH_RNDIS | 
 | 20 | #    undef USB_ETH_RNDIS | 
 | 21 | #  endif | 
 | 22 | #  ifdef CONFIG_USB_FUNCTIONFS_RNDIS | 
 | 23 | #    define USB_ETH_RNDIS y | 
 | 24 | #  endif | 
 | 25 |  | 
 | 26 | #  include "f_ecm.c" | 
 | 27 | #  include "f_subset.c" | 
 | 28 | #  ifdef USB_ETH_RNDIS | 
 | 29 | #    include "f_rndis.c" | 
 | 30 | #    include "rndis.c" | 
 | 31 | #  endif | 
 | 32 | #  include "u_ether.c" | 
 | 33 |  | 
 | 34 | static u8 gfs_hostaddr[ETH_ALEN]; | 
 | 35 | #else | 
 | 36 | #  if !defined CONFIG_USB_FUNCTIONFS_GENERIC | 
 | 37 | #    define CONFIG_USB_FUNCTIONFS_GENERIC | 
 | 38 | #  endif | 
 | 39 | #  define gether_cleanup() do { } while (0) | 
 | 40 | #  define gether_setup(gadget, hostaddr)   ((int)0) | 
 | 41 | #endif | 
 | 42 |  | 
 | 43 | #include "f_fs.c" | 
 | 44 |  | 
 | 45 |  | 
 | 46 | #define DRIVER_NAME	"g_ffs" | 
 | 47 | #define DRIVER_DESC	"USB Function Filesystem" | 
 | 48 | #define DRIVER_VERSION	"24 Aug 2004" | 
 | 49 |  | 
 | 50 | MODULE_DESCRIPTION(DRIVER_DESC); | 
 | 51 | MODULE_AUTHOR("Michal Nazarewicz"); | 
 | 52 | MODULE_LICENSE("GPL"); | 
 | 53 |  | 
 | 54 |  | 
 | 55 | static unsigned short gfs_vendor_id    = 0x0525;	/* XXX NetChip */ | 
 | 56 | static unsigned short gfs_product_id   = 0xa4ac;	/* XXX */ | 
 | 57 |  | 
 | 58 | static struct usb_device_descriptor gfs_dev_desc = { | 
 | 59 | 	.bLength		= sizeof gfs_dev_desc, | 
 | 60 | 	.bDescriptorType	= USB_DT_DEVICE, | 
 | 61 |  | 
 | 62 | 	.bcdUSB			= cpu_to_le16(0x0200), | 
 | 63 | 	.bDeviceClass		= USB_CLASS_PER_INTERFACE, | 
 | 64 |  | 
 | 65 | 	/* Vendor and product id can be overridden by module parameters.  */ | 
 | 66 | 	/* .idVendor		= cpu_to_le16(gfs_vendor_id), */ | 
 | 67 | 	/* .idProduct		= cpu_to_le16(gfs_product_id), */ | 
 | 68 | 	/* .bcdDevice		= f(hardware) */ | 
 | 69 | 	/* .iManufacturer	= DYNAMIC */ | 
 | 70 | 	/* .iProduct		= DYNAMIC */ | 
 | 71 | 	/* NO SERIAL NUMBER */ | 
 | 72 | 	.bNumConfigurations	= 1, | 
 | 73 | }; | 
 | 74 |  | 
 | 75 | #define GFS_MODULE_PARAM_DESC(name, field) \ | 
 | 76 | 	MODULE_PARM_DESC(name, "Value of the " #field " field of the device descriptor sent to the host.  Takes effect only prior to the user-space driver registering to the FunctionFS.") | 
 | 77 |  | 
 | 78 | module_param_named(usb_class,    gfs_dev_desc.bDeviceClass,    byte,   0644); | 
 | 79 | GFS_MODULE_PARAM_DESC(usb_class, bDeviceClass); | 
 | 80 | module_param_named(usb_subclass, gfs_dev_desc.bDeviceSubClass, byte,   0644); | 
 | 81 | GFS_MODULE_PARAM_DESC(usb_subclass, bDeviceSubClass); | 
 | 82 | module_param_named(usb_protocol, gfs_dev_desc.bDeviceProtocol, byte,   0644); | 
 | 83 | GFS_MODULE_PARAM_DESC(usb_protocol, bDeviceProtocol); | 
 | 84 | module_param_named(usb_vendor,   gfs_vendor_id,                ushort, 0644); | 
 | 85 | GFS_MODULE_PARAM_DESC(usb_vendor, idVendor); | 
 | 86 | module_param_named(usb_product,  gfs_product_id,               ushort, 0644); | 
 | 87 | GFS_MODULE_PARAM_DESC(usb_product, idProduct); | 
 | 88 |  | 
 | 89 |  | 
 | 90 |  | 
 | 91 | static const struct usb_descriptor_header *gfs_otg_desc[] = { | 
 | 92 | 	(const struct usb_descriptor_header *) | 
 | 93 | 	&(const struct usb_otg_descriptor) { | 
 | 94 | 		.bLength		= sizeof(struct usb_otg_descriptor), | 
 | 95 | 		.bDescriptorType	= USB_DT_OTG, | 
 | 96 |  | 
 | 97 | 		/* REVISIT SRP-only hardware is possible, although | 
 | 98 | 		 * it would not be called "OTG" ... */ | 
 | 99 | 		.bmAttributes		= USB_OTG_SRP | USB_OTG_HNP, | 
 | 100 | 	}, | 
 | 101 |  | 
 | 102 | 	NULL | 
 | 103 | }; | 
 | 104 |  | 
 | 105 | /* string IDs are assigned dynamically */ | 
 | 106 |  | 
 | 107 | enum { | 
 | 108 | 	GFS_STRING_MANUFACTURER_IDX, | 
 | 109 | 	GFS_STRING_PRODUCT_IDX, | 
 | 110 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS | 
 | 111 | 	GFS_STRING_RNDIS_CONFIG_IDX, | 
 | 112 | #endif | 
 | 113 | #ifdef CONFIG_USB_FUNCTIONFS_ETH | 
 | 114 | 	GFS_STRING_ECM_CONFIG_IDX, | 
 | 115 | #endif | 
 | 116 | #ifdef CONFIG_USB_FUNCTIONFS_GENERIC | 
 | 117 | 	GFS_STRING_GENERIC_CONFIG_IDX, | 
 | 118 | #endif | 
 | 119 | }; | 
 | 120 |  | 
 | 121 | static       char gfs_manufacturer[50]; | 
 | 122 | static const char gfs_driver_desc[] = DRIVER_DESC; | 
 | 123 | static const char gfs_short_name[]  = DRIVER_NAME; | 
 | 124 |  | 
 | 125 | static struct usb_string gfs_strings[] = { | 
 | 126 | 	[GFS_STRING_MANUFACTURER_IDX].s = gfs_manufacturer, | 
 | 127 | 	[GFS_STRING_PRODUCT_IDX].s = gfs_driver_desc, | 
 | 128 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS | 
 | 129 | 	[GFS_STRING_RNDIS_CONFIG_IDX].s = "FunctionFS + RNDIS", | 
 | 130 | #endif | 
 | 131 | #ifdef CONFIG_USB_FUNCTIONFS_ETH | 
 | 132 | 	[GFS_STRING_ECM_CONFIG_IDX].s = "FunctionFS + ECM", | 
 | 133 | #endif | 
 | 134 | #ifdef CONFIG_USB_FUNCTIONFS_GENERIC | 
 | 135 | 	[GFS_STRING_GENERIC_CONFIG_IDX].s = "FunctionFS", | 
 | 136 | #endif | 
 | 137 | 	{  } /* end of list */ | 
 | 138 | }; | 
 | 139 |  | 
 | 140 | static struct usb_gadget_strings *gfs_dev_strings[] = { | 
 | 141 | 	&(struct usb_gadget_strings) { | 
 | 142 | 		.language	= 0x0409,	/* en-us */ | 
 | 143 | 		.strings	= gfs_strings, | 
 | 144 | 	}, | 
 | 145 | 	NULL, | 
 | 146 | }; | 
 | 147 |  | 
 | 148 |  | 
 | 149 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS | 
 | 150 | static int gfs_do_rndis_config(struct usb_configuration *c); | 
 | 151 |  | 
 | 152 | static struct usb_configuration gfs_rndis_config_driver = { | 
 | 153 | 	.label			= "FunctionFS + RNDIS", | 
 | 154 | 	.bind			= gfs_do_rndis_config, | 
 | 155 | 	.bConfigurationValue	= 1, | 
 | 156 | 	/* .iConfiguration	= DYNAMIC */ | 
 | 157 | 	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER, | 
 | 158 | }; | 
 | 159 | #  define gfs_add_rndis_config(cdev) \ | 
 | 160 | 	usb_add_config(cdev, &gfs_rndis_config_driver) | 
 | 161 | #else | 
 | 162 | #  define gfs_add_rndis_config(cdev) 0 | 
 | 163 | #endif | 
 | 164 |  | 
 | 165 |  | 
 | 166 | #ifdef CONFIG_USB_FUNCTIONFS_ETH | 
 | 167 | static int gfs_do_ecm_config(struct usb_configuration *c); | 
 | 168 |  | 
 | 169 | static struct usb_configuration gfs_ecm_config_driver = { | 
 | 170 | 	.label			= "FunctionFS + ECM", | 
 | 171 | 	.bind			= gfs_do_ecm_config, | 
 | 172 | 	.bConfigurationValue	= 1, | 
 | 173 | 	/* .iConfiguration	= DYNAMIC */ | 
 | 174 | 	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER, | 
 | 175 | }; | 
 | 176 | #  define gfs_add_ecm_config(cdev) \ | 
 | 177 | 	usb_add_config(cdev, &gfs_ecm_config_driver) | 
 | 178 | #else | 
 | 179 | #  define gfs_add_ecm_config(cdev) 0 | 
 | 180 | #endif | 
 | 181 |  | 
 | 182 |  | 
 | 183 | #ifdef CONFIG_USB_FUNCTIONFS_GENERIC | 
 | 184 | static int gfs_do_generic_config(struct usb_configuration *c); | 
 | 185 |  | 
 | 186 | static struct usb_configuration gfs_generic_config_driver = { | 
 | 187 | 	.label			= "FunctionFS", | 
 | 188 | 	.bind			= gfs_do_generic_config, | 
 | 189 | 	.bConfigurationValue	= 2, | 
 | 190 | 	/* .iConfiguration	= DYNAMIC */ | 
 | 191 | 	.bmAttributes		= USB_CONFIG_ATT_SELFPOWER, | 
 | 192 | }; | 
 | 193 | #  define gfs_add_generic_config(cdev) \ | 
 | 194 | 	usb_add_config(cdev, &gfs_generic_config_driver) | 
 | 195 | #else | 
 | 196 | #  define gfs_add_generic_config(cdev) 0 | 
 | 197 | #endif | 
 | 198 |  | 
 | 199 |  | 
 | 200 | static int gfs_bind(struct usb_composite_dev *cdev); | 
 | 201 | static int gfs_unbind(struct usb_composite_dev *cdev); | 
 | 202 |  | 
 | 203 | static struct usb_composite_driver gfs_driver = { | 
 | 204 | 	.name		= gfs_short_name, | 
 | 205 | 	.dev		= &gfs_dev_desc, | 
 | 206 | 	.strings	= gfs_dev_strings, | 
 | 207 | 	.bind		= gfs_bind, | 
 | 208 | 	.unbind		= gfs_unbind, | 
 | 209 | }; | 
 | 210 |  | 
 | 211 |  | 
 | 212 | static struct ffs_data *gfs_ffs_data; | 
 | 213 | static unsigned long gfs_registered; | 
 | 214 |  | 
 | 215 |  | 
 | 216 | static int  gfs_init(void) | 
 | 217 | { | 
 | 218 | 	ENTER(); | 
 | 219 |  | 
 | 220 | 	return functionfs_init(); | 
 | 221 | } | 
 | 222 | module_init(gfs_init); | 
 | 223 |  | 
 | 224 | static void  gfs_exit(void) | 
 | 225 | { | 
 | 226 | 	ENTER(); | 
 | 227 |  | 
 | 228 | 	if (test_and_clear_bit(0, &gfs_registered)) | 
 | 229 | 		usb_composite_unregister(&gfs_driver); | 
 | 230 |  | 
 | 231 | 	functionfs_cleanup(); | 
 | 232 | } | 
 | 233 | module_exit(gfs_exit); | 
 | 234 |  | 
 | 235 |  | 
 | 236 | static int functionfs_ready_callback(struct ffs_data *ffs) | 
 | 237 | { | 
 | 238 | 	int ret; | 
 | 239 |  | 
 | 240 | 	ENTER(); | 
 | 241 |  | 
 | 242 | 	if (WARN_ON(test_and_set_bit(0, &gfs_registered))) | 
 | 243 | 		return -EBUSY; | 
 | 244 |  | 
 | 245 | 	gfs_ffs_data = ffs; | 
 | 246 | 	ret = usb_composite_register(&gfs_driver); | 
 | 247 | 	if (unlikely(ret < 0)) | 
 | 248 | 		clear_bit(0, &gfs_registered); | 
 | 249 | 	return ret; | 
 | 250 | } | 
 | 251 |  | 
 | 252 | static void functionfs_closed_callback(struct ffs_data *ffs) | 
 | 253 | { | 
 | 254 | 	ENTER(); | 
 | 255 |  | 
 | 256 | 	if (test_and_clear_bit(0, &gfs_registered)) | 
 | 257 | 		usb_composite_unregister(&gfs_driver); | 
 | 258 | } | 
 | 259 |  | 
 | 260 |  | 
 | 261 | static int functionfs_check_dev_callback(const char *dev_name) | 
 | 262 | { | 
 | 263 | 	return 0; | 
 | 264 | } | 
 | 265 |  | 
 | 266 |  | 
 | 267 |  | 
 | 268 | static int gfs_bind(struct usb_composite_dev *cdev) | 
 | 269 | { | 
 | 270 | 	int ret; | 
 | 271 |  | 
 | 272 | 	ENTER(); | 
 | 273 |  | 
 | 274 | 	if (WARN_ON(!gfs_ffs_data)) | 
 | 275 | 		return -ENODEV; | 
 | 276 |  | 
 | 277 | 	ret = gether_setup(cdev->gadget, gfs_hostaddr); | 
 | 278 | 	if (unlikely(ret < 0)) | 
 | 279 | 		goto error_quick; | 
 | 280 |  | 
 | 281 | 	gfs_dev_desc.idVendor  = cpu_to_le16(gfs_vendor_id); | 
 | 282 | 	gfs_dev_desc.idProduct = cpu_to_le16(gfs_product_id); | 
 | 283 |  | 
 | 284 | 	snprintf(gfs_manufacturer, sizeof gfs_manufacturer, "%s %s with %s", | 
 | 285 | 		 init_utsname()->sysname, init_utsname()->release, | 
 | 286 | 		 cdev->gadget->name); | 
 | 287 | 	ret = usb_string_id(cdev); | 
 | 288 | 	if (unlikely(ret < 0)) | 
 | 289 | 		goto error; | 
 | 290 | 	gfs_strings[GFS_STRING_MANUFACTURER_IDX].id = ret; | 
 | 291 | 	gfs_dev_desc.iManufacturer = ret; | 
 | 292 |  | 
 | 293 | 	ret = usb_string_id(cdev); | 
 | 294 | 	if (unlikely(ret < 0)) | 
 | 295 | 		goto error; | 
 | 296 | 	gfs_strings[GFS_STRING_PRODUCT_IDX].id = ret; | 
 | 297 | 	gfs_dev_desc.iProduct = ret; | 
 | 298 |  | 
 | 299 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS | 
 | 300 | 	ret = usb_string_id(cdev); | 
 | 301 | 	if (unlikely(ret < 0)) | 
 | 302 | 		goto error; | 
 | 303 | 	gfs_strings[GFS_STRING_RNDIS_CONFIG_IDX].id = ret; | 
 | 304 | 	gfs_rndis_config_driver.iConfiguration = ret; | 
 | 305 | #endif | 
 | 306 |  | 
 | 307 | #ifdef CONFIG_USB_FUNCTIONFS_ETH | 
 | 308 | 	ret = usb_string_id(cdev); | 
 | 309 | 	if (unlikely(ret < 0)) | 
 | 310 | 		goto error; | 
 | 311 | 	gfs_strings[GFS_STRING_ECM_CONFIG_IDX].id = ret; | 
 | 312 | 	gfs_ecm_config_driver.iConfiguration = ret; | 
 | 313 | #endif | 
 | 314 |  | 
 | 315 | #ifdef CONFIG_USB_FUNCTIONFS_GENERIC | 
 | 316 | 	ret = usb_string_id(cdev); | 
 | 317 | 	if (unlikely(ret < 0)) | 
 | 318 | 		goto error; | 
 | 319 | 	gfs_strings[GFS_STRING_GENERIC_CONFIG_IDX].id = ret; | 
 | 320 | 	gfs_generic_config_driver.iConfiguration = ret; | 
 | 321 | #endif | 
 | 322 |  | 
 | 323 | 	ret = functionfs_bind(gfs_ffs_data, cdev); | 
 | 324 | 	if (unlikely(ret < 0)) | 
 | 325 | 		goto error; | 
 | 326 |  | 
 | 327 | 	ret = gfs_add_rndis_config(cdev); | 
 | 328 | 	if (unlikely(ret < 0)) | 
 | 329 | 		goto error_unbind; | 
 | 330 |  | 
 | 331 | 	ret = gfs_add_ecm_config(cdev); | 
 | 332 | 	if (unlikely(ret < 0)) | 
 | 333 | 		goto error_unbind; | 
 | 334 |  | 
 | 335 | 	ret = gfs_add_generic_config(cdev); | 
 | 336 | 	if (unlikely(ret < 0)) | 
 | 337 | 		goto error_unbind; | 
 | 338 |  | 
 | 339 | 	return 0; | 
 | 340 |  | 
 | 341 | error_unbind: | 
 | 342 | 	functionfs_unbind(gfs_ffs_data); | 
 | 343 | error: | 
 | 344 | 	gether_cleanup(); | 
 | 345 | error_quick: | 
 | 346 | 	gfs_ffs_data = NULL; | 
 | 347 | 	return ret; | 
 | 348 | } | 
 | 349 |  | 
 | 350 | static int gfs_unbind(struct usb_composite_dev *cdev) | 
 | 351 | { | 
 | 352 | 	ENTER(); | 
 | 353 |  | 
 | 354 | 	/* We may have been called in an error recovery frem | 
 | 355 | 	 * composite_bind() after gfs_unbind() failure so we need to | 
 | 356 | 	 * check if gfs_ffs_data is not NULL since gfs_bind() handles | 
 | 357 | 	 * all error recovery itself.  I'd rather we werent called | 
 | 358 | 	 * from composite on orror recovery, but what you're gonna | 
 | 359 | 	 * do...? */ | 
 | 360 |  | 
 | 361 | 	if (gfs_ffs_data) { | 
 | 362 | 		gether_cleanup(); | 
 | 363 | 		functionfs_unbind(gfs_ffs_data); | 
 | 364 | 		gfs_ffs_data = NULL; | 
 | 365 | 	} | 
 | 366 |  | 
 | 367 | 	return 0; | 
 | 368 | } | 
 | 369 |  | 
 | 370 |  | 
 | 371 | static int __gfs_do_config(struct usb_configuration *c, | 
 | 372 | 			   int (*eth)(struct usb_configuration *c, u8 *ethaddr), | 
 | 373 | 			   u8 *ethaddr) | 
 | 374 | { | 
 | 375 | 	int ret; | 
 | 376 |  | 
 | 377 | 	if (WARN_ON(!gfs_ffs_data)) | 
 | 378 | 		return -ENODEV; | 
 | 379 |  | 
 | 380 | 	if (gadget_is_otg(c->cdev->gadget)) { | 
 | 381 | 		c->descriptors = gfs_otg_desc; | 
 | 382 | 		c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | 
 | 383 | 	} | 
 | 384 |  | 
 | 385 | 	if (eth) { | 
 | 386 | 		ret = eth(c, ethaddr); | 
 | 387 | 		if (unlikely(ret < 0)) | 
 | 388 | 			return ret; | 
 | 389 | 	} | 
 | 390 |  | 
 | 391 | 	ret = functionfs_add(c->cdev, c, gfs_ffs_data); | 
 | 392 | 	if (unlikely(ret < 0)) | 
 | 393 | 		return ret; | 
 | 394 |  | 
 | 395 | 	return 0; | 
 | 396 | } | 
 | 397 |  | 
 | 398 | #ifdef CONFIG_USB_FUNCTIONFS_RNDIS | 
 | 399 | static int gfs_do_rndis_config(struct usb_configuration *c) | 
 | 400 | { | 
 | 401 | 	ENTER(); | 
 | 402 |  | 
 | 403 | 	return __gfs_do_config(c, rndis_bind_config, gfs_hostaddr); | 
 | 404 | } | 
 | 405 | #endif | 
 | 406 |  | 
 | 407 | #ifdef CONFIG_USB_FUNCTIONFS_ETH | 
 | 408 | static int gfs_do_ecm_config(struct usb_configuration *c) | 
 | 409 | { | 
 | 410 | 	ENTER(); | 
 | 411 |  | 
 | 412 | 	return __gfs_do_config(c, | 
 | 413 | 			       can_support_ecm(c->cdev->gadget) | 
 | 414 | 			     ? ecm_bind_config : geth_bind_config, | 
 | 415 | 			       gfs_hostaddr); | 
 | 416 | } | 
 | 417 | #endif | 
 | 418 |  | 
 | 419 | #ifdef CONFIG_USB_FUNCTIONFS_GENERIC | 
 | 420 | static int gfs_do_generic_config(struct usb_configuration *c) | 
 | 421 | { | 
 | 422 | 	ENTER(); | 
 | 423 |  | 
 | 424 | 	return __gfs_do_config(c, NULL, NULL); | 
 | 425 | } | 
 | 426 | #endif |