/* * File Name : multi_config.c * * Virtual multi configuration utilities for composite USB gadgets. * This utilitie can support variable interface for variable Host PC. * * Copyright (C) 2011 Samsung Electronics * Author: SoonYong, Cho * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include "multi_config.h" static int multi; /* current configuration */ static int is_multi; /* Is multi configuration available ? */ static int stringMode = OTHER_REQUEST; static int interfaceCount; /* Description : Set configuration number * Parameter : unsigned num (host request) * Return value : always return 0 (It's virtual multiconfiguration) */ unsigned set_config_number(unsigned num) { if (is_multi_configuration()) { USB_DBG_ESS("multi config_num=%d(zero base)\n", num); if (num < MAX_MULTI_CONFIG_NUM) multi = num; /* save config number from Host request */ } else { USB_DBG_ESS("single config num=%d\n", num); multi = 0; /* single config */ } return 0; /* always return 0 config */ } /* Description : Get configuration number * Return value : virtual multiconfiguration number (zero base) */ int get_config_number(void) { USB_DBG("multi=%d\n", multi); return multi; } /* Description : Check configuration number * Parameter : unsigned num (host request) * Return value : 1 (true : virtual multi configuraiton) 0 (false : normal configuration) */ int check_config(unsigned num) { USB_DBG("num=%d, multi=%d\n", num, multi); /* multi is zero base, but num is 1 ase */ if (num && num == multi + 1) { USB_DBG("Use virtual multi configuration\n"); return 1; } else { USB_DBG("normal configuration\n"); return 0; } } /* Description : Search number of configuration including virtual configuration * Parameter : usb_configuration *c (referenced configuration) unsigned count (real number of configuration) * Return value : virtual or real number of configuration */ unsigned count_multi_config(struct usb_configuration *c, unsigned count) { int f_first = 0; int f_second = 0; int f_exception = 0; struct usb_function *f; is_multi = 0; if (!c) { USB_DBG("usb_configuration is not valid\n"); return 0; } list_for_each_entry(f, &c->functions, list) { if (!strcmp(f->name, MULTI_FUNCTION_1)) { USB_DBG("%s +\n", MULTI_FUNCTION_1); f_first = 1; } else if (!strcmp(f->name, MULTI_FUNCTION_2)) { USB_DBG("%s +\n", MULTI_FUNCTION_2); f_second = 1; } else if (!strcmp(f->name, MULTI_EXCEPTION_FUNCTION) || !strcmp(f->name, MULTI_EXCEPTION_FUNCTION_F_FS)) { USB_DBG("exception %s +\n", f->name ); f_exception = 1; } } if (f_first && f_second && !f_exception) { USB_DBG_ESS("ready multi\n"); is_multi = 1; return 2; } return count; } /* Description : Is multi configuration available ? * Return value : 1 (true), 0 (false) */ int is_multi_configuration(void) { USB_DBG("= %d\n", is_multi); return is_multi; } /* Description : Check function to skip for multi configuration * Parameter : char* name (function name) * Return value : 0 (not available), 1 (available) */ int is_available_function(const char *name) { if (is_multi_configuration()) { USB_DBG("multi case\n"); if (!multi) { if (!strcmp(name, MAIN_FUNCTION)) { USB_DBG("%s is available.\n", MAIN_FUNCTION); return 1; } return 0; /* anothor function is not available */ } else { USB_DBG("multi=%d all available\n", multi); } } return 1; /* if single configuration, every function is available */ } /* Description : Change configuration using virtual multi configuration. * Parameter : struct usb_funciton f (to be changed function interface) void *next (next means usb req->buf) int len (length for to fill buffer) struct usb_configuration *config (To reference interface array of current config) enum usb_device_speed speed (usb speed) * Return value : "ret < 0" means fillbuffer function is failed. */ int change_conf(struct usb_function *f, void *next, int len, struct usb_configuration *config, enum usb_device_speed speed) { u8 *dest; int status = 0; struct usb_descriptor_header *descriptor; struct usb_interface_descriptor *intf; int index_intf = 0; int change_intf = 0; struct usb_descriptor_header **descriptors; if (!f || !config || !next) { USB_DBG_ESS("one of f, config, next is not valid\n"); return -EFAULT; } USB_DBG("f->%s process multi\n", f->name); if (speed == USB_SPEED_HIGH) descriptors = f->hs_descriptors; else descriptors = f->fs_descriptors; if (!descriptors) { USB_DBG_ESS("descriptor is not available\n"); return -EFAULT; } if (f->set_config_desc) f->set_config_desc(stringMode); /* set interface numbers dynamically */ dest = next; while ((descriptor = *descriptors++) != NULL) { intf = (struct usb_interface_descriptor *)dest; if (intf->bDescriptorType == USB_DT_INTERFACE) { if (intf->bAlternateSetting == 0) { intf->bInterfaceNumber = interfaceCount++; USB_DBG("a=0 intf->bInterfaceNumber=%d\n", intf->bInterfaceNumber); } else { intf->bInterfaceNumber = interfaceCount - 1; USB_DBG("a!=0 intf->bInterfaceNumber=%d\n", intf->bInterfaceNumber); } config->interface [intf->bInterfaceNumber] = f; if (f->set_intf_num) { change_intf = 1; f->set_intf_num(f, intf->bInterfaceNumber, index_intf++); } } dest += intf->bLength; } if (change_intf) { if (speed == USB_SPEED_HIGH) descriptors = f->hs_descriptors; else descriptors = f->fs_descriptors; status = usb_descriptor_fillbuf( next, len, (const struct usb_descriptor_header **) descriptors); if (status < 0) { USB_DBG_ESS("usb_descriptor_fillbuf failed\n"); return status; } } return status; } /* Description : Set interface count * Parameter : struct usb_configuration *config * (To reference interface array of current config) * struct usb_config_descriptor *c * (number of interfaces) * Return value : void */ void set_interface_count(struct usb_configuration *config, struct usb_config_descriptor *c) { USB_DBG_ESS("next_interface_id=%d\n", interfaceCount); config->next_interface_id = interfaceCount; config->interface[interfaceCount] = 0; c->bNumInterfaces = interfaceCount; interfaceCount = 0; return ; } /* Description : Set string mode * This mode will be used for deciding other interface. * Parameter : u16 w_length * - 2 means MAC request. * - Windows and Linux PC always request 255 size. */ void set_string_mode(u16 w_length) { if (w_length == 2) { USB_DBG("mac request\n"); stringMode = MAC_REQUEST; } else if (w_length == 0) { USB_DBG("initialize string mode\n"); stringMode = OTHER_REQUEST; } } /* Description : Get Host OS type * Return value : type - u16 * - 0 : MAC PC * - 1 : Windows and Linux PC */ u16 get_host_os_type(void) { return stringMode; }