diff --git a/brick/kernel/Makefile b/brick/kernel/Makefile new file mode 100644 index 00000000..47510958 --- /dev/null +++ b/brick/kernel/Makefile @@ -0,0 +1,2 @@ +obj-m += $(MOD).o + diff --git a/brick/kernel/computil.c b/brick/kernel/computil.c new file mode 100644 index 00000000..f60c4bc0 --- /dev/null +++ b/brick/kernel/computil.c @@ -0,0 +1,1382 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * This Composite Utility file is based on and inheritated from + * the original file and work done by David Brownell + * + * >> composite.c - infrastructure for Composite USB Gadgets << + * >> Copyright (C) 2006-2008 David Brownell << + * + */ + +#ifndef PCASM +#include +#include +#include +#include + +#include +#else +// Keep Eclipse happy +#endif + +#undef dev_vdbg +#undef dev_dbg +#define dev_vdbg(d, args...) printk(args) +#define dev_dbg(d, args...) printk(args) + + +/* big enough to hold our biggest descriptor */ +#define USB_BUFSIZ 1024 + +USB_SPEED UsbSpeed; // We will only be HIGH speed if device is connected to a HIGH + // speed HOST (make no sense to use FULL speed for PC connectivity.) + // FULL speed is necessary due to the fact, that we only have a 12 + // Mbit HOST port on the brick (I.e. FULL speed HOST) +static USB_SPEED UsbSpeedDefault; +static USB_SPEED *pUsbSpeed = &UsbSpeedDefault; +/* + * The code in this file is utility code, used to build a gadget driver + * from one or more "function" drivers, one or more "configuration" + * objects, and a "usb_composite_driver" by gluing them together along + * with the relevant device-wide data. + */ + +static struct usb_composite_driver *composite; + +/* Some systems will need runtime overrides for the product identifers + * published in the device descriptor, either numbers or strings or both. + * String parameters are in UTF-8 (superset of ASCII's 7 bit characters). + */ +#ifndef PCASM +static ushort idVendor; +module_param(idVendor, ushort, 0); +MODULE_PARM_DESC(idVendor, "USB Vendor ID"); + +static ushort idProduct; +module_param(idProduct, ushort, 0); +MODULE_PARM_DESC(idProduct, "USB Product ID"); + +static ushort bcdDevice; +module_param(bcdDevice, ushort, 0); +MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); + +static char *iManufacturer; +module_param(iManufacturer, charp, 0); +MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); + +static char *iProduct; +module_param(iProduct, charp, 0); +MODULE_PARM_DESC(iProduct, "USB Product string"); + +static char *iSerialNumber; +module_param(iSerialNumber, charp, 0); +MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); +#else +// Keep Eclipse happy +#endif + +/*HIGH SPEED hid descriptor */ /* Size = 29 decimal */ +static char hs_hid_report_descriptor[] = { + 0x06, 0x00, 0xFF, // Usage page (vendor defined) + 0x09, 0x01, // Usage ID (vendor defined) + 0xA1, 0x01, // Collection (application) + + // The Input report + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8 bits) + + 0x96, 0x00, 0x04, // Report Count (1024 fields) + 0x09, 0x01, // USAGE (vendor usage 1) + 0x81, 0x02, // Input (Data, Variable, Absolute) + + 0x96, 0x00, 0x04, // Report Count (1024 fields) + 0x09, 0x01, // USAGE (vendor usage 1) + 0x91, 0x02, // Output (Data, Variable, Absolute) + + 0xc0 /* END_COLLECTION */ + + +}; + +/*LOW SPEED hid descriptor */ /* Size = 29 decimal */ +static char fs_hid_report_descriptor[] = { + 0x06, 0x00, 0xFF, // Usage page (vendor defined) + 0x09, 0x01, // Usage ID (vendor defined) + 0xA1, 0x01, // Collection (application) + + // The Input report + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8 bits) + + 0x96, 0x40, 0x00, // Report Count (64 fields) + 0x09, 0x01, // USAGE (vendor usage 1) + 0x81, 0x02, // Input (Data, Variable, Absolute) + + 0x96, 0x40, 0x00, // Report Count (64 fields) + 0x09, 0x01, // USAGE (vendor usage 1) + 0x91, 0x02, // Output (Data, Variable, Absolute) + + 0xc0 /* END_COLLECTION */ + +}; + + +/*-------------------------------------------------------------------------*/ + +/** + * usb_add_function() - add a function to a configuration + * @config: the configuration + * @function: the function being added + * Context: single threaded during gadget setup + * + * After initialization, each configuration must have one or more + * functions added to it. Adding a function involves calling its @bind() + * method to allocate resources such as interface and string identifiers + * and endpoints. + * + * This function returns the value of the function's bind(), which is + * zero for success else a negative errno value. + */ + +int usb_add_function(struct usb_configuration *config, + struct usb_function *function) +{ + int value = -EINVAL; + + DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n", + function->name, function, + config->label, config); + + if (!function->set_alt || !function->disable) + goto done; + + function->config = config; + list_add_tail(&function->list, &config->functions); + + /* REVISIT *require* function->bind? */ + if (function->bind) { + value = function->bind(config, function); + if (value < 0) { + list_del(&function->list); + function->config = NULL; + } + } else + value = 0; + + /* We allow configurations that don't work at both speeds. + * If we run into a lowspeed Linux system, treat it the same + * as full speed ... it's the function drivers that will need + * to avoid bulk and ISO transfers. + */ + if (!config->fullspeed && function->descriptors) + config->fullspeed = true; + if (!config->highspeed && function->hs_descriptors) + config->highspeed = true; + +done: + if (value) + DBG(config->cdev, "adding '%s'/%p --> %d\n", + function->name, function, value); + return value; +} + +/* + * usb_function_deactivate - prevent function and gadget enumeration + * @function: the function that isn't yet ready to respond + * + * Blocks response of the gadget driver to host enumeration by + * preventing the data line pullup from being activated. This is + * normally called during @bind() processing to change from the + * initial "ready to respond" state, or when a required resource + * becomes available. + * + * For example, drivers that serve as a passthrough to a userspace + * daemon can block enumeration unless that daemon (such as an OBEX, + * MTP, or print server) is ready to handle host requests. + * + * Not all systems support software control of their USB peripheral + * data pullups. + * + * Returns zero on success, else negative errno. + */ +int usb_function_deactivate(struct usb_function *function) +{ + struct usb_composite_dev *cdev = function->config->cdev; + unsigned long flags; + int status = 0; + + spin_lock_irqsave(&cdev->lock, flags); + + if (cdev->deactivations == 0) + status = usb_gadget_disconnect(cdev->gadget); + if (status == 0) + cdev->deactivations++; + + spin_unlock_irqrestore(&cdev->lock, flags); + return status; +} + +/* + * usb_function_activate - allow function and gadget enumeration + * @function: function on which usb_function_activate() was called + * + * Reverses effect of usb_function_deactivate(). If no more functions + * are delaying their activation, the gadget driver will respond to + * host enumeration procedures. + * + * Returns zero on success, else negative errno. + */ +int usb_function_activate(struct usb_function *function) +{ + struct usb_composite_dev *cdev = function->config->cdev; + int status = 0; + + spin_lock(&cdev->lock); + + if (WARN_ON(cdev->deactivations == 0)) + status = -EINVAL; + else { + cdev->deactivations--; + if (cdev->deactivations == 0) + status = usb_gadget_connect(cdev->gadget); + } + + spin_unlock(&cdev->lock); + return status; +} + +/** + * usb_interface_id() - allocate an unused interface ID + * @config: configuration associated with the interface + * @function: function handling the interface + * Context: single threaded during gadget setup + * + * usb_interface_id() is called from usb_function.bind() callbacks to + * allocate new interface IDs. The function driver will then store that + * ID in interface, association, CDC union, and other descriptors. It + * will also handle any control requests targetted at that interface, + * particularly changing its altsetting via set_alt(). There may + * also be class-specific or vendor-specific requests to handle. + * + * All interface identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. Note that since interface + * identifers are configuration-specific, functions used in more than + * one configuration (or more than once in a given configuration) need + * multiple versions of the relevant descriptors. + * + * Returns the interface ID which was allocated; or -ENODEV if no + * more interface IDs can be allocated. + */ + +int usb_interface_id(struct usb_configuration *config, + struct usb_function *function) +{ + unsigned id = config->next_interface_id; + + if (id < MAX_CONFIG_INTERFACES) { + config->interface[id] = function; + config->next_interface_id = id + 1; + return id; + } + return -ENODEV; +} + +#ifndef PCASM +static int config_buf(struct usb_configuration *config, + enum usb_device_speed speed, void *buf, u8 type) +{ + struct usb_config_descriptor *c = buf; + void *next = buf + USB_DT_CONFIG_SIZE; + int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE; + struct usb_function *f; + int status; + + /* write the config descriptor */ + c = buf; + c->bLength = USB_DT_CONFIG_SIZE; + c->bDescriptorType = type; + /* wTotalLength is written later */ + c->bNumInterfaces = config->next_interface_id; // CTRL version was set to 2 + c->bConfigurationValue = config->bConfigurationValue; + c->iConfiguration = config->iConfiguration; + c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes; + c->bMaxPower = config->bMaxPower ? : (CONFIG_USB_GADGET_VBUS_DRAW / 2); + + /* There may be e.g. OTG descriptors */ + if (config->descriptors) { + status = usb_descriptor_fillbuf(next, len, + config->descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + /* add each function's descriptors */ + + list_for_each_entry(f, &config->functions, list) { + struct usb_descriptor_header **descriptors; + + if (speed == USB_SPEED_HIGH) + { + descriptors = f->hs_descriptors; + } + else + { + descriptors = f->descriptors; + } + + if (!descriptors) + continue; + status = usb_descriptor_fillbuf(next, len, + (const struct usb_descriptor_header **) descriptors); + if (status < 0) + return status; + len -= status; + next += status; + } + + len = next - buf; + c->wTotalLength = cpu_to_le16(len); + return len; +} +#else +// Keep Eclipse happy +#endif + +#ifndef PCASM + +static int config_desc(struct usb_composite_dev *cdev, unsigned w_value) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c; + u8 type = w_value >> 8; + enum usb_device_speed speed = USB_SPEED_UNKNOWN; + + if (gadget_is_dualspeed(gadget)) { + int hs = 0; + + /*if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + if (hs) + speed = USB_SPEED_HIGH; + */ + + if (gadget->speed == USB_SPEED_HIGH) + { + hs = 1; + UsbSpeed.Speed = HIGH_SPEED; + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("Speed = HIGH\n\r"); + #endif + } + else + { + hs = 0; + UsbSpeed.Speed = FULL_SPEED; + #undef DEBUG + //#define DEBUG + #ifdef DEBUG + printk("Speed = LOW\n\r"); + #endif + } + + if (type == USB_DT_OTHER_SPEED_CONFIG) + hs = !hs; + if (hs) + speed = USB_SPEED_HIGH; + } + + /* This is a lookup by config *INDEX* */ + w_value &= 0xff; + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (speed == USB_SPEED_HIGH) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + if (w_value == 0) + return config_buf(c, speed, cdev->req->buf, type); + w_value--; + } + return -EINVAL; +} +#else +// Keep Eclipse happy +#endif + +#ifndef PCASM +static int count_configs(struct usb_composite_dev *cdev, unsigned type) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c; + unsigned count = 0; + int hs = 0; + + if (gadget_is_dualspeed(gadget)) { + if (gadget->speed == USB_SPEED_HIGH) + hs = 1; + if (type == USB_DT_DEVICE_QUALIFIER) + hs = !hs; + } + list_for_each_entry(c, &cdev->configs, list) { + /* ignore configs that won't work at this speed */ + if (hs) { + if (!c->highspeed) + continue; + } else { + if (!c->fullspeed) + continue; + } + count++; + } + return count; +} +#else +// Keep Eclipse happy +#endif + +static void device_qual(struct usb_composite_dev *cdev) +{ + struct usb_qualifier_descriptor *qual = cdev->req->buf; + + qual->bLength = sizeof(*qual); + qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER; + /* POLICY: same bcdUSB and device type info at both speeds */ + qual->bcdUSB = cdev->desc.bcdUSB; + qual->bDeviceClass = cdev->desc.bDeviceClass; + qual->bDeviceSubClass = cdev->desc.bDeviceSubClass; + qual->bDeviceProtocol = cdev->desc.bDeviceProtocol; + /* ASSUME same EP0 fifo size at both speeds */ + qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0; + qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER); + qual->bRESERVED = 0; +} + +/*-------------------------------------------------------------------------*/ +#ifndef PCASM +static void reset_config(struct usb_composite_dev *cdev) +{ + struct usb_function *f; + + DBG(cdev, "reset config\n"); + + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->disable) + f->disable(f); + + bitmap_zero(f->endpoints, 32); + } + cdev->config = NULL; +} +#else +// Keep Eclipse happy +#endif + +#ifndef PCASM +static int set_config(struct usb_composite_dev *cdev, + const struct usb_ctrlrequest *ctrl, unsigned number) +{ + struct usb_gadget *gadget = cdev->gadget; + struct usb_configuration *c = NULL; + int result = -EINVAL; + unsigned power = gadget_is_otg(gadget) ? 8 : 100; + int tmp; + + if (cdev->config) + reset_config(cdev); + + if (number) { + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == number) { + result = 0; + break; + } + } + if (result < 0) + goto done; + } else + result = 0; + + /*INFO(cdev, "%s speed config #%d: %s\n", + ({ char *speed; + switch (gadget->speed) { + case USB_SPEED_LOW: speed = "low"; break; + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; + } ; speed; }), number, c ? c->label : "unconfigured"); +*/ + + if (!c) + goto done; + + cdev->config = c; + + /* Initialize all interfaces by setting them to altsetting zero. */ + for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) { + struct usb_function *f = c->interface[tmp]; + struct usb_descriptor_header **descriptors; + + if (!f) + break; + + /* + * Record which endpoints are used by the function. This is used + * to dispatch control requests targeted at that endpoint to the + * function's setup callback instead of the current + * configuration's setup callback. + */ + + if (gadget->speed == USB_SPEED_HIGH) + descriptors = f->hs_descriptors; + else + descriptors = f->descriptors; + + for (; *descriptors; ++descriptors) { + struct usb_endpoint_descriptor *ep; + int addr; + + if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT) + continue; + + ep = (struct usb_endpoint_descriptor *)*descriptors; + addr = ((ep->bEndpointAddress & 0x80) >> 3) + | (ep->bEndpointAddress & 0x0f); + set_bit(addr, f->endpoints); + } + + result = f->set_alt(f, tmp, 0); + + + + if (result < 0) { + DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", + tmp, f->name, f, result); + + reset_config(cdev); + + goto done; + } + } + + /* when we return, be sure our power usage is valid */ + power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW; +done: + usb_gadget_vbus_draw(gadget, power); + return result; +} + +#else +// Keep Eclipse happy +#endif + + +/** + * usb_add_config() - add a configuration to a device. + * @cdev: wraps the USB gadget + * @config: the configuration, with bConfigurationValue assigned + * Context: single threaded during gadget setup + * + * One of the main tasks of a composite driver's bind() routine is to + * add each of the configurations it supports, using this routine. + * + * This function returns the value of the configuration's bind(), which + * is zero for success else a negative errno value. Binding configurations + * assigns global resources including string IDs, and per-configuration + * resources such as interface IDs and endpoints. + */ + +#ifndef PCASM +int usb_add_config(struct usb_composite_dev *cdev, + struct usb_configuration *config) +{ + int status = -EINVAL; + struct usb_configuration *c; + + DBG(cdev, "adding config #%u '%s'/%p\n", + config->bConfigurationValue, + config->label, config); + + if (!config->bConfigurationValue || !config->bind) + goto done; + + /* Prevent duplicate configuration identifiers */ + list_for_each_entry(c, &cdev->configs, list) { + if (c->bConfigurationValue == config->bConfigurationValue) { + status = -EBUSY; + goto done; + } + } + + config->cdev = cdev; + list_add_tail(&config->list, &cdev->configs); + + INIT_LIST_HEAD(&config->functions); + config->next_interface_id = 0; + + status = config->bind(config); + if (status < 0) { + list_del(&config->list); + config->cdev = NULL; + } else { + unsigned i; + + DBG(cdev, "cfg %d/%p speeds:%s%s\n", + config->bConfigurationValue, config, + config->highspeed ? " high" : "", + config->fullspeed + ? (gadget_is_dualspeed(cdev->gadget) + ? " full" + : " full/low") + : ""); + + for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { + struct usb_function *f = config->interface[i]; + + if (!f) + continue; + DBG(cdev, " interface %d = %s/%p\n", + i, f->name, f); + } + } + + /* set_alt(), or next config->bind(), sets up + * ep->driver_data as needed. + */ + usb_ep_autoconfig_reset(cdev->gadget); + +done: + if (status) + DBG(cdev, "added config '%s'/%u --> %d\n", config->label, + config->bConfigurationValue, status); + return status; +} +#else +// Keep Eclipse happy +#endif + +/*-------------------------------------------------------------------------*/ + +/* We support strings in multiple languages ... string descriptor zero + * says which languages are supported. The typical case will be that + * only one language (probably English) is used, with I18N handled on + * the host side. + */ + +static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf) +{ + const struct usb_gadget_strings *s; + u16 language; + __le16 *tmp; + + while (*sp) { + s = *sp; + language = cpu_to_le16(s->language); + for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) { + if (*tmp == language) + goto repeat; + } + *tmp++ = language; +repeat: + sp++; + } +} + +static int lookup_string( + struct usb_gadget_strings **sp, + void *buf, + u16 language, + int id +) +{ + struct usb_gadget_strings *s; + int value; + + while (*sp) { + s = *sp++; + if (s->language != language) + continue; + value = usb_gadget_get_string(s, id, buf); + if (value > 0) + return value; + } + return -EINVAL; +} + +#ifndef PCASM +static int get_string(struct usb_composite_dev *cdev, + void *buf, u16 language, int id) +{ + struct usb_configuration *c; + struct usb_function *f; + int len; + + /* Yes, not only is USB's I18N support probably more than most + * folk will ever care about ... also, it's all supported here. + * (Except for UTF8 support for Unicode's "Astral Planes".) + */ + + /* 0 == report all available language codes */ + if (id == 0) { + struct usb_string_descriptor *s = buf; + struct usb_gadget_strings **sp; + + memset(s, 0, 256); + s->bDescriptorType = USB_DT_STRING; + + sp = composite->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(c, &cdev->configs, list) { + sp = c->strings; + if (sp) + collect_langs(sp, s->wData); + + list_for_each_entry(f, &c->functions, list) { + sp = f->strings; + if (sp) + collect_langs(sp, s->wData); + } + } + + for (len = 0; len <= 126 && s->wData[len]; len++) + continue; + if (!len) + return -EINVAL; + + s->bLength = 2 * (len + 1); + return s->bLength; + } + + /* Otherwise, look up and return a specified string. String IDs + * are device-scoped, so we look up each string table we're told + * about. These lookups are infrequent; simpler-is-better here. + */ + if (composite->strings) { + len = lookup_string(composite->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(c, &cdev->configs, list) { + if (c->strings) { + len = lookup_string(c->strings, buf, language, id); + if (len > 0) + return len; + } + list_for_each_entry(f, &c->functions, list) { + if (!f->strings) + continue; + len = lookup_string(f->strings, buf, language, id); + if (len > 0) + return len; + } + } + return -EINVAL; +} +#else +// Keep Eclipse happy +#endif + +/** + * usb_string_id() - allocate an unused string ID + * @cdev: the device whose string descriptor IDs are being allocated + * Context: single threaded during gadget setup + * + * @usb_string_id() is called from bind() callbacks to allocate + * string IDs. Drivers for functions, configurations, or gadgets will + * then store that ID in the appropriate descriptors and string table. + * + * All string identifier should be allocated using this routine, to + * ensure that for example different functions don't wrongly assign + * different meanings to the same identifier. + */ + +int usb_string_id(struct usb_composite_dev *cdev) +{ + if (cdev->next_string_id < 254) { + /* string id 0 is reserved */ + cdev->next_string_id++; + return cdev->next_string_id; + } + return -ENODEV; +} + +/*-------------------------------------------------------------------------*/ + +static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + DBG((struct usb_composite_dev *) ep->driver_data, + "setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +/* + * The setup() callback implements all the ep0 functionality that's + * not handled lower down, in hardware or the hardware driver(like + * device and endpoint feature flags, and their status). It's all + * housekeeping for the gadget function we're implementing. Most of + * the work is in config and function specific setup. + */ + +#ifndef PCASM +static int +composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_request *req = cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u8 intf = w_index & 0xFF; + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + struct usb_function *f = NULL; + u8 endp; + + /* partial re-init of the response message; the function or the + * gadget might need to intercept e.g. a control-OUT completion + * when we delegate to it. + */ + req->zero = 0; + req->complete = composite_setup_complete; + req->length = USB_BUFSIZ; + gadget->ep0->driver_data = cdev; + + + VDBG(cdev, + "setup req %02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + +switch (ctrl->bRequest) { + /* we handle all standard USB descriptors */ + case USB_REQ_GET_DESCRIPTOR: + + if ((ctrl->bRequestType != USB_DIR_IN) && (ctrl->bRequestType != 0x81)) + goto unknown; + + switch (w_value >> 8) { + + case USB_DT_DEVICE: // + + cdev->desc.bNumConfigurations = + count_configs(cdev, USB_DT_DEVICE); + value = min(w_length, (u16) sizeof cdev->desc); + memcpy(req->buf, &cdev->desc, value); + + break; + case USB_DT_DEVICE_QUALIFIER: + + if (!gadget_is_dualspeed(gadget)) + break; + device_qual(cdev); + value = min_t(int, w_length, + sizeof(struct usb_qualifier_descriptor)); + + break; + case USB_DT_OTHER_SPEED_CONFIG: + + if (!gadget_is_dualspeed(gadget)) + break; + /* FALLTHROUGH */ + case USB_DT_CONFIG: + + value = config_desc(cdev, w_value); + if (value >= 0) + value = min(w_length, (u16) value); + break; + case USB_DT_STRING: + + value = get_string(cdev, req->buf, + w_index, w_value & 0xff); + if (value >= 0) + value = min(w_length, (u16) value); + break; + + case 34: + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("\nWE are in GET_REPORT_DESC !! SETUP in COMPUTIL.C - case 34"); + #endif + + if(UsbSpeed.Speed == HIGH_SPEED) + { + memcpy(req->buf, hs_hid_report_descriptor, 29); + + #ifdef DEBUG + printk("\nWE are @ HIGH REPORT_DESC !!"); + #endif + } + else + { + memcpy(req->buf, fs_hid_report_descriptor, 29); + + #ifdef DEBUG + printk("\nWE are @ FULL REPORT_DESC !!"); + #endif + } + value =29; + break; + } + break; + + /* any number of configs can work */ + case USB_REQ_SET_CONFIGURATION: + + if (ctrl->bRequestType != 0) + goto unknown; + + if (gadget_is_otg(gadget)) { + + if (gadget->a_hnp_support) + DBG(cdev, "HNP available\n"); + else if (gadget->a_alt_hnp_support) + DBG(cdev, "HNP on another port\n"); + else + VDBG(cdev, "HNP inactive\n"); + } + + spin_lock(&cdev->lock); + value = set_config(cdev, ctrl, w_value); + + spin_unlock(&cdev->lock); + break; + case USB_REQ_GET_CONFIGURATION: + + if (ctrl->bRequestType != USB_DIR_IN) + goto unknown; + + if (cdev->config) + *(u8 *)req->buf = cdev->config->bConfigurationValue; + else + *(u8 *)req->buf = 0; + value = min(w_length, (u16) 1); + break; + + /* function drivers must handle get/set altsetting; if there's + * no get() method, we know only altsetting zero works. + */ + case USB_REQ_SET_INTERFACE: + + if (ctrl->bRequestType != USB_RECIP_INTERFACE) + goto unknown; + + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + + f = cdev->config->interface[intf]; + if (!f) + break; + + if (w_value && !f->set_alt) + break; + + value = f->set_alt(f, w_index, w_value); + break; + case USB_REQ_GET_INTERFACE: + + if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) + goto unknown; + + if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES) + break; + + f = cdev->config->interface[intf]; + if (!f) + break; + + /* lots of interfaces only need altsetting zero... */ + value = f->get_alt ? f->get_alt(f, w_index) : 0; + if (value < 0) + break; + + *((u8 *)req->buf) = value; + value = min(w_length, (u16) 1); + break; + default: +unknown: + VDBG(cdev, + "non-core control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + + /* functions always handle their interfaces and endpoints... + * punt other recipients (other, WUSB, ...) to the current + * configuration code. + * + * REVISIT it could make sense to let the composite device + * take such requests too, if that's ever needed: to work + * in config 0, etc. + */ + switch (ctrl->bRequestType & USB_RECIP_MASK) { + + case USB_RECIP_INTERFACE: + + f = cdev->config->interface[intf]; + break; + + case USB_RECIP_ENDPOINT: + + endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f); + list_for_each_entry(f, &cdev->config->functions, list) { + if (test_bit(endp, f->endpoints)) + break; + } + + if (&f->list == &cdev->config->functions) + f = NULL; + break; + } + + if (f && f->setup) + value = f->setup(f, ctrl); + else { + + struct usb_configuration *c; + + c = cdev->config; + if (c && c->setup) + value = c->setup(c, ctrl); + } + + goto done; + } + + /* respond with data transfer before status phase? */ + if (value >= 0) { + + req->length = value; + req->zero = value < w_length; + value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + + if (value < 0) { + DBG(cdev, "ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(gadget->ep0, req); + } + } + +done: + + /* device either stalls (value < 0) or reports success */ + + return value; + +} +#else +// Keep Eclipse happy +#endif + + +static void composite_disconnect(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + unsigned long flags; + + /* REVISIT: should we have config and device level + * disconnect callbacks? + */ + + spin_lock_irqsave(&cdev->lock, flags); + if (cdev->config) + reset_config(cdev); + spin_unlock_irqrestore(&cdev->lock, flags); +} + +/*-------------------------------------------------------------------------*/ +#ifndef PCASM +static void /* __init_or_exit */ +composite_unbind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + + /* composite_disconnect() must already have been called + * by the underlying peripheral controller driver! + * so there's no i/o concurrency that could affect the + * state protected by cdev->lock. + */ + WARN_ON(cdev->config); + + while (!list_empty(&cdev->configs)) { + struct usb_configuration *c; + + c = list_first_entry(&cdev->configs, + struct usb_configuration, list); + while (!list_empty(&c->functions)) { + struct usb_function *f; + + f = list_first_entry(&c->functions, + struct usb_function, list); + list_del(&f->list); + if (f->unbind) { + DBG(cdev, "unbind function '%s'/%p\n", + f->name, f); + f->unbind(c, f); + /* may free memory for "f" */ + } + } + list_del(&c->list); + if (c->unbind) { + DBG(cdev, "unbind config '%s'/%p\n", c->label, c); + c->unbind(c); + /* may free memory for "c" */ + } + } + if (composite->unbind) + composite->unbind(cdev); + + if (cdev->req) { + kfree(cdev->req->buf); + usb_ep_free_request(gadget->ep0, cdev->req); + } + kfree(cdev); + set_gadget_data(gadget, NULL); + composite = NULL; +} +#else +// Keep Eclipse happy +#endif + +static void +string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s) +{ + struct usb_string *str = tab->strings; + + for (str = tab->strings; str->s; str++) { + if (str->id == id) { + str->s = s; + return; + } + } +} + +static void +string_override(struct usb_gadget_strings **tab, u8 id, const char *s) +{ + while (*tab) { + string_override_one(*tab, id, s); + tab++; + } +} + +static int composite_bind(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev; + int status = -ENOMEM; + + cdev = kzalloc(sizeof *cdev, GFP_KERNEL); + if (!cdev) + return status; + + spin_lock_init(&cdev->lock); + cdev->gadget = gadget; + set_gadget_data(gadget, cdev); + INIT_LIST_HEAD(&cdev->configs); + + /* preallocate control response and buffer */ + cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!cdev->req) + goto fail; + cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); + if (!cdev->req->buf) + goto fail; + cdev->req->complete = composite_setup_complete; + gadget->ep0->driver_data = cdev; + + cdev->bufsiz = USB_BUFSIZ; + cdev->driver = composite; + + usb_gadget_set_selfpowered(gadget); + + /* interface and string IDs start at zero via kzalloc. + * we force endpoints to start unassigned; few controller + * drivers will zero ep->driver_data. + */ + usb_ep_autoconfig_reset(cdev->gadget); + + /* composite gadget needs to assign strings for whole device (like + * serial number), register function drivers, potentially update + * power state and consumption, etc + */ + status = composite->bind(cdev); + if (status < 0) + goto fail; + + cdev->desc = *composite->dev; + cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + + /* standardized runtime overrides for device ID data */ + if (idVendor) + cdev->desc.idVendor = cpu_to_le16(idVendor); + if (idProduct) + cdev->desc.idProduct = cpu_to_le16(idProduct); + if (bcdDevice) + cdev->desc.bcdDevice = cpu_to_le16(bcdDevice); + + /* strings can't be assigned before bind() allocates the + * releavnt identifiers + */ + if (cdev->desc.iManufacturer && iManufacturer) + string_override(composite->strings, + cdev->desc.iManufacturer, iManufacturer); + if (cdev->desc.iProduct && iProduct) + string_override(composite->strings, + cdev->desc.iProduct, iProduct); + if (cdev->desc.iSerialNumber && iSerialNumber) + string_override(composite->strings, + cdev->desc.iSerialNumber, iSerialNumber); + + // INFO(cdev, "%s ready\n", composite->name); + return 0; + +fail: + composite_unbind(gadget); + return status; +} + +/*-------------------------------------------------------------------------*/ + +#ifndef PCASM +static void +composite_suspend(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + /* REVISIT: should we have config level + * suspend/resume callbacks? + */ + DBG(cdev, "suspend\n"); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->suspend) + f->suspend(f); + } + } + if (composite->suspend) + composite->suspend(cdev); +} +#else +// Keep Eclipse happy +#endif + +#ifndef PCASM +static void +composite_resume(struct usb_gadget *gadget) +{ + struct usb_composite_dev *cdev = get_gadget_data(gadget); + struct usb_function *f; + + /* REVISIT: should we have config level + * suspend/resume callbacks? + */ + DBG(cdev, "resume\n"); + if (composite->resume) + composite->resume(cdev); + if (cdev->config) { + list_for_each_entry(f, &cdev->config->functions, list) { + if (f->resume) + f->resume(f); + } + } +} +#else +// Keep Eclipse happy +#endif + +/*-------------------------------------------------------------------------*/ + +static struct usb_gadget_driver composite_driver = { + .speed = USB_SPEED_HIGH, + + .bind = composite_bind, + /* .unbind = __exit_p(composite_unbind), */ + .unbind = composite_unbind, + + .setup = composite_setup, + .disconnect = composite_disconnect, + + .suspend = composite_suspend, + .resume = composite_resume, + + .driver = { + .owner = THIS_MODULE, + }, +}; + +/** + * usb_composite_register() - register a composite driver + * @driver: the driver to register + * Context: single threaded during gadget setup + * + * This function is used to register drivers using the composite driver + * framework. The return value is zero, or a negative errno value. + * Those values normally come from the driver's @bind method, which does + * all the work of setting up the driver to match the hardware. + * + * On successful return, the gadget is ready to respond to requests from + * the host, unless one of its components invokes usb_gadget_disconnect() + * while it was binding. That would usually be done in order to wait for + * some userspace participation. + */ +int usb_composite_register(struct usb_composite_driver *driver) +{ + if (!driver || !driver->dev || !driver->bind || composite) + return -EINVAL; + + if (!driver->name) + driver->name = "composite"; + composite_driver.function = (char *) driver->name; + composite_driver.driver.name = driver->name; + composite = driver; + + return usb_gadget_register_driver(&composite_driver); +} + +/** + * usb_composite_unregister() - unregister a composite driver + * @driver: the driver to unregister + * + * This function is used to unregister drivers using the composite + * driver framework. + */ +void usb_composite_unregister(struct usb_composite_driver *driver) +{ + if (composite != driver) + return; + usb_gadget_unregister_driver(&composite_driver); +} + diff --git a/brick/kernel/d_usbdev.c b/brick/kernel/d_usbdev.c new file mode 100644 index 00000000..32bbdb42 --- /dev/null +++ b/brick/kernel/d_usbdev.c @@ -0,0 +1,755 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * This UsbDev file is based on and inheritated from + * the original file (zero.c) and work done by David Brownell + * + * >> zero.c -- Gadget Zero, for USB development << + * + * >> Copyright (C) 2003-2008 David Brownell << + * >> Copyright (C) 2008 by Nokia Corporation << + * + */ + +/*! \page UsbdevModule USB device Module + * + * + *- \subpage UsbdevModuleResources + */ + + +#ifndef PCASM +#include +#endif + +#include "source/lms2012.h" +#include "source/am1808.h" + + +#define MODULE_NAME "usbdev_module" +#define DEVICE1_NAME USBDEV_DEVICE + + + +static int ModuleInit(void); +static void ModuleExit(void); + +#define __USE_POSIX + +#include +#include + +#include + + +#ifndef PCASM +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void myReset(void); +static int dUsbInit(void); +static void dUsbExit(void); +static struct fsg_common *fsg_common; + +#include "computil.c" // The composite framework used as utility file +#include <../drivers/usb/gadget/gadget_chips.h> +#include <../drivers/usb/gadget/usbstring.c> +#include <../drivers/usb/gadget/config.c> +#include <../drivers/usb/gadget/epautoconf.c> + +#include "f_mass_storage.c" + +/*-------------------------------------------------------------------------*/ + +/* + * Kbuild is not very cooperative with respect to linking separately + * compiled library objects into one module. So for now we won't use + * separate compilation ... ensuring init/exit sections work to shrink + * the runtime footprint, and giving us at least some parts of what + * a "gcc --combine ... part1.c part2.c part3.c ... " build would. + */ + +#include <../drivers/usb/gadget/g_zero.h> +#define MAX_EP_SIZE 1024 +#define MAX_FULLSPEED_EP_SIZE 64 +unsigned buflen = MAX_EP_SIZE ; +char usb_char_buffer_in[MAX_EP_SIZE]; +char usb_full_buffer_in[MAX_FULLSPEED_EP_SIZE]; +int usb_char_in_length = 0; +char usb_char_buffer_out[MAX_EP_SIZE]; +char usb_full_buffer_out[MAX_FULLSPEED_EP_SIZE]; +int usb_char_out_length = 0; + +#define SHM_LENGTH (sizeof(UsbSpeedDefault)) +#define NPAGES ((SHM_LENGTH + PAGE_SIZE - 1) / PAGE_SIZE) +static void *kmalloc_ptr; + +#include "usb_function.c" // Specific USB functionality + +/*-------------------------------------------------------------------------*/ + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("The LEGO Group"); +MODULE_DESCRIPTION(MODULE_NAME); +MODULE_SUPPORTED_DEVICE(DEVICE1_NAME); + +module_init(ModuleInit); +module_exit(ModuleExit); + +#else +// Keep Eclipse happy +#endif + +// USB main stuff + +#define DRIVER_VERSION "31jan2011->" + +#ifndef PCASM +module_param(buflen, uint, 0); +#else +// Keep Eclipse happy +#endif + +static int loopdefault = 0; +#ifndef PCASM +module_param(loopdefault, bool, S_IRUGO|S_IWUSR); +#else +// Keep Eclipse happy +#endif + +#define DRIVER_VENDOR_NUM 0x0694 // LEGO Group +#define DRIVER_PRODUCT_NUM 0x0005 // No. 5 in a row +#define DEFAULT_AUTORESUME 0 + +/* If the optional "autoresume" mode is enabled, it provides good + * functional coverage for the "USBCV" test harness from USB-IF. + * It's always set if OTG mode is enabled. + */ + +unsigned autoresume = DEFAULT_AUTORESUME; +module_param(autoresume, uint, S_IRUGO); + +#ifndef PCASM +MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup"); +#else +// Keep Eclipse happy +#endif + +/*-------------------------------------------------------------------------*/ + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = cpu_to_le16(0x0200), + .bDeviceClass = 0xEF, + .bDeviceSubClass = 2, + .bDeviceProtocol = 1, + + /*.bMaxPacketSize0 = f(hardware) */ + .idVendor = cpu_to_le16(DRIVER_VENDOR_NUM), + .idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM), + + .bNumConfigurations = 1, +}; + +#ifdef CONFIG_USB_OTG +struct usb_otg_descriptor otg_descriptor = { + .bLength = sizeof otg_descriptor, + .bDescriptorType = USB_DT_OTG, + + /* REVISIT SRP-only hardware is possible, although + * it would not be called "OTG" ... + */ + .bmAttributes = USB_OTG_SRP | USB_OTG_HNP, +}; + +const struct usb_descriptor_header *otg_desc[] = { + (struct usb_descriptor_header *) &otg_descriptor, + NULL, +}; +#endif + +/* string IDs are assigned dynamically */ + +#define STRING_MANUFACTURER_IDX 0 +#define STRING_PRODUCT_IDX 1 +#define STRING_SERIAL_IDX 2 + +static char manufacturer[] = "LEGO Group"; +static char serial[] = "123456789ABC "; +static char longname[] = "EV3 brick "; + +static struct usb_string strings_dev[3] = { + [STRING_MANUFACTURER_IDX].s = manufacturer, + [STRING_PRODUCT_IDX].s = longname, + [STRING_SERIAL_IDX].s = serial +}; + + +static struct usb_gadget_strings stringtab_dev = { + .language = 0x0409, /* en-us */ + .strings = strings_dev, +}; + +static struct usb_gadget_strings *dev_strings[] = { + &stringtab_dev, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +struct usb_request *alloc_ep_req(struct usb_ep *ep) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (req) { + req->length = buflen; + req->buf = kmalloc(buflen, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + } + return req; +} + +void free_ep_req(struct usb_ep *ep, struct usb_request *req) +{ + kfree(req->buf); + usb_ep_free_request(ep, req); +} + +static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) +{ + int value; + + if (ep->driver_data) { + value = usb_ep_disable(ep); + if (value < 0) + DBG(cdev, "disable %s --> %d\n", + ep->name, value); + ep->driver_data = NULL; + } +} + +void disable_endpoints(struct usb_composite_dev *cdev, + struct usb_ep *in, struct usb_ep *out) +{ + disable_ep(cdev, in); + disable_ep(cdev, out); +} + +/*-------------------------------------------------------------------------*/ + +static struct timer_list autoresume_timer; + +static void zero_autoresume(unsigned long _c) +{ + struct usb_composite_dev *cdev = (void *)_c; + struct usb_gadget *g = cdev->gadget; + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("zero_autoresume\n\r"); + #endif + + /* unconfigured devices can't issue wakeups */ + if (!cdev->config) + return; + + /* Normally the host would be woken up for something + * more significant than just a timer firing; likely + * because of some direct user request. + */ + if (g->speed != USB_SPEED_UNKNOWN) { + int status = usb_gadget_wakeup(g); + INFO(cdev, "%s --> %d\n", __func__, status); + } +} + +static void zero_suspend(struct usb_composite_dev *cdev) +{ + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("zero_suspend\n\r"); + #endif + + if (cdev->gadget->speed == USB_SPEED_UNKNOWN) + return; + + if (autoresume) { + mod_timer(&autoresume_timer, jiffies + (HZ * autoresume)); + DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume); + } else + DBG(cdev, "%s\n", __func__); +} + +static void zero_resume(struct usb_composite_dev *cdev) +{ + DBG(cdev, "%s\n", __func__); + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("zero_resume\n\r"); + #endif + + del_timer(&autoresume_timer); +} + +/*-------------------------------------------------------------------------*/ + +static int msg_bind(struct usb_composite_dev *cdev); +static void msg_bind2(struct usb_composite_dev *cdev); + +static int zero_bind(struct usb_composite_dev *cdev) +{ + int gcnum; + struct usb_gadget *gadget = cdev->gadget; + int id; + + /* Allocate string descriptor numbers ... note that string + * contents can be overridden by the composite_dev glue. + */ + id = usb_string_id(cdev); + if (id < 0) + return id; + + strings_dev[STRING_MANUFACTURER_IDX].id = id; + device_desc.iManufacturer = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + + + strings_dev[STRING_PRODUCT_IDX].id = id; + device_desc.iProduct = id; + + id = usb_string_id(cdev); + if (id < 0) + return id; + + + strings_dev[STRING_SERIAL_IDX].id = id; + device_desc.iSerialNumber = id; + + id = msg_bind(cdev); + if (id < 0) + return id; + + setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev); + + rudolf_add(cdev, autoresume != 0); + + gcnum = usb_gadget_controller_number(gadget); + if (gcnum >= 0) + device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum); + else { + /* gadget zero is so simple (for now, no altsettings) that + * it SHOULD NOT have problems with bulk-capable hardware. + * so just warn about unrcognized controllers -- don't panic. + * + * things like configuration and altsetting numbering + * can need hardware-specific attention though. + */ + pr_warning("%s: controller '%s' not recognized\n", + longname, gadget->name); + device_desc.bcdDevice = cpu_to_le16(0x9999); + } + + msg_bind2(cdev); + + return 0; +} + +static int zero_unbind(struct usb_composite_dev *cdev) +{ + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("zero_unbind\n\r"); + #endif + + del_timer_sync(&autoresume_timer); + return 0; +} + +static struct usb_composite_driver zero_driver = { + .name = "zero", + .dev = &device_desc, + .strings = dev_strings, + .bind = zero_bind, + .unbind = zero_unbind, + .suspend = zero_suspend, + .resume = zero_resume, +}; + +static int dUsbInit(void) +{ + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("dUsbInit\n\r"); + #endif + + UsbSpeed.Speed = FULL_SPEED; // default to FULL_SPEED if not connected to a HIGH-SPEED + (*pUsbSpeed).Speed = FULL_SPEED; // HOST. If not connected to HIGH-SPEED we assume we're + // wanting (or at least doing) Daisy Chain + return usb_composite_register(&zero_driver); +} + +static void dUsbExit(void) +{ + usb_composite_unregister(&zero_driver); +} + +// DEVICE1 char device stuff ******************************************************************** + +static ssize_t Device1Write(struct file *File,const char *Buffer,size_t Count,loff_t *Data) +{ + // Write data for the HOST to poll - Stuff sent to the HOST + + int BytesWritten = 0; + + #undef DEBUG + //#define DEBUG + #ifdef DEBUG + printk("Device1Write - usb_char_in_length = %d\n", usb_char_in_length); + #endif + + if (usb_char_in_length == 0) // ready for more + { // else wait in USER layer + BytesWritten = Count; + copy_from_user(usb_char_buffer_in, Buffer, BytesWritten); + usb_char_in_length = BytesWritten; + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("WR = %d, %d -- ", usb_char_buffer_in[2], usb_char_buffer_in[3]); + #endif + + if(USB_DATA_PENDING == input_state) + { + // Already we've a failed tx (HOST part starwing?? + + input_state = USB_DATA_READY; + #undef DEBUG + //#define DEBUG + #ifdef DEBUG + printk("DATA_PENDING SECOND time and reset!! in Device1Write\n\r"); + #endif + } + + if(USB_DATA_READY == input_state) + { + #undef DEBUG + //#define DEBUG + #ifdef DEBUG + printk("USB_DATA_READY in Device1Write\n\r"); + #endif + + input_state = USB_DATA_BUSY; + write_data_to_the_host(save_in_ep, save_in_req); + usb_req_arm(save_in_ep, save_in_req); // new request + } + else + { + input_state = USB_DATA_PENDING; + + #undef DEBUG + //#define DEBUG + #ifdef DEBUG + printk("DATA_PENDING in Device1Write\n\r"); + #endif + } + } + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("usbdev %d written\n\r", BytesWritten); + #endif + + return (BytesWritten); // Zero means USB was not ready yet +} + +static ssize_t Device1Read(struct file *File,char *Buffer,size_t Count,loff_t *Offset) +{ + // Read the bits'n'bytes from the HOST + int BytesRead = 0; + + if (usb_char_out_length > 0) // Something to look at + { + #undef DEBUG + //#define DEBUG + #ifdef DEBUG + printk("Some bytes to READ?\n\r"); + #endif + + copy_to_user(Buffer, usb_char_buffer_out, Count); + BytesRead = usb_char_out_length; + usb_char_out_length = 0; + } + return (BytesRead); +} + +static int Device1Mmap(struct file *filp, struct vm_area_struct *vma) +{ + int ret; + + ret = remap_pfn_range(vma,vma->vm_start,virt_to_phys((void*)((unsigned long)pUsbSpeed)) >> PAGE_SHIFT,vma->vm_end-vma->vm_start,PAGE_SHARED); + + if (ret != 0) + { + ret = -EAGAIN; + } + + return (ret); +} +static const struct file_operations Device1Entries = +{ + .owner = THIS_MODULE, + .read = Device1Read, + .write = Device1Write, + .mmap = Device1Mmap +}; + + +static struct miscdevice Device1 = +{ + MISC_DYNAMIC_MINOR, + DEVICE1_NAME, + &Device1Entries +}; + + +static int Device1Init(void) +{ + int Result = -1; + UWORD *pTemp; + int i; + + Result = misc_register(&Device1); + if (Result) + { + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk(" %s device register failed\n",DEVICE1_NAME); + #endif + } + else + { + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk(" %s device register OK\n",DEVICE1_NAME); + #endif + + // allocate kernel shared memory for DaisyChain Speed info + + if ((kmalloc_ptr = kmalloc((NPAGES + 2) * PAGE_SIZE, GFP_KERNEL)) != NULL) + { + + pTemp = (UWORD*)((((unsigned long)kmalloc_ptr) + PAGE_SIZE - 1) & PAGE_MASK); + + for (i = 0; i < NPAGES * PAGE_SIZE; i += PAGE_SIZE) + { + SetPageReserved(virt_to_page(((unsigned long)pTemp) + i)); + } + + pUsbSpeed = (USB_SPEED*)pTemp; + } + + dUsbInit(); + } + + return (Result); +} + +static void Device1Exit(void) +{ + int i; + UWORD *pTemp = (UWORD*)pUsbSpeed; + + dUsbExit(); + + pUsbSpeed = &UsbSpeedDefault; + + for (i = 0; i < NPAGES * PAGE_SIZE; i+= PAGE_SIZE) + { + ClearPageReserved(virt_to_page(((unsigned long)pTemp) + i)); + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk(" %s memory page %d unmapped\n",DEVICE1_NAME,i); + #endif + } + + kfree(kmalloc_ptr); + + misc_deregister(&Device1); + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk(" %s device unregistered\n",DEVICE1_NAME); + #endif +} + + +// MODULE ********************************************************************* + +char *HostStr; // Used for HostName - or NOT used at all +char *SerialStr; // Used for Serial number (I.e. BT number) + +module_param (HostStr, charp, 0); +module_param (SerialStr, charp, 0); + +static int ModuleInit(void) +{ + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("%s Module init started\r\n",MODULE_NAME); + #endif + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("This is DEFAULT NAME: %s\n\r", longname); + #endif + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("\n\rThis is the HostStr: %s\n\r", HostStr); + #endif + + strcpy(longname, HostStr); + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("\n\rThis is the INSMODed NAME: %s\n\r", longname); + #endif + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("\n\rThis is the DEFAULT SerialNumber: %s\n\r", serial); + #endif + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("\n\rThis is the SerialStr: %s\n\r", SerialStr); + #endif + + strcpy(serial, SerialStr); + + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("\n\rThis is the INSMODed SerialNumber (BT mac): %s\n\r", serial); + #endif + + Device1Init(); + + return (0); +} + +static void ModuleExit(void) +{ + //#define DEBUG + #undef DEBUG + #ifdef DEBUG + printk("%s exit started\n",MODULE_NAME); + #endif + + Device1Exit(); + +} + + + + +// MSG + + +/****************************** Configurations ******************************/ + +static struct fsg_module_parameters fsg_mod_data = { + .stall = 1 +}; +FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data); + +/****************************** Gadget Bind ******************************/ + + +static void msg_bind2(struct usb_composite_dev *cdev) +{ + fsg_common_put(fsg_common); +} + +static int msg_bind(struct usb_composite_dev *cdev) +{ + /* set up mass storage function */ + fsg_common = fsg_common_from_params(0, cdev, &fsg_mod_data); + if (IS_ERR(fsg_common)) { + return PTR_ERR(fsg_common); + } + return 0; +} + + +static int msg_config(struct usb_configuration *c) { + return fsg_add(c->cdev, c, fsg_common); +} + +void myReset() { + dUsbExit(); + dUsbInit(); +} \ No newline at end of file diff --git a/brick/kernel/f_mass_storage.c b/brick/kernel/f_mass_storage.c new file mode 100644 index 00000000..c08a31ce --- /dev/null +++ b/brick/kernel/f_mass_storage.c @@ -0,0 +1,3147 @@ +/* + * f_mass_storage.c -- Mass Storage USB Composite Function + * + * Copyright (C) 2003-2008 Alan Stern + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +/* + * The Mass Storage Function acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In + * addition to providing an example of a genuinely useful composite + * function for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. + * + * Function supports multiple logical units (LUNs). Backing storage + * for each LUN is provided by a regular file or a block device. + * Access for each LUN can be limited to read-only. Moreover, the + * function can indicate that LUN is removable and/or CD-ROM. (The + * later implies read-only access.) + * + * MSF is configured by specifying a fsg_config structure. It has the + * following fields: + * + * nluns Number of LUNs function have (anywhere from 1 + * to FSG_MAX_LUNS which is 8). + * luns An array of LUN configuration values. This + * should be filled for each LUN that + * function will include (ie. for "nluns" + * LUNs). Each element of the array has + * the following fields: + * ->filename The path to the backing file for the LUN. + * Required if LUN is not marked as + * removable. + * ->ro Flag specifying access to the LUN shall be + * read-only. This is implied if CD-ROM + * emulation is enabled as well as when + * it was impossible to open "filename" + * in R/W mode. + * ->removable Flag specifying that LUN shall be indicated as + * being removable. + * ->cdrom Flag specifying that LUN shall be reported as + * being a CD-ROM. + * + * lun_name_format A printf-like format for names of the LUN + * devices. This determines how the + * directory in sysfs will be named. + * Unless you are using several MSFs in + * a single gadget (as opposed to single + * MSF in many configurations) you may + * leave it as NULL (in which case + * "lun%d" will be used). In the format + * you can use "%d" to index LUNs for + * MSF's with more than one LUN. (Beware + * that there is only one integer given + * as an argument for the format and + * specifying invalid format may cause + * unspecified behaviour.) + * thread_name Name of the kernel thread process used by the + * MSF. You can safely set it to NULL + * (in which case default "file-storage" + * will be used). + * + * vendor_name + * product_name + * release Information used as a reply to INQUIRY + * request. To use default set to NULL, + * NULL, 0xffff respectively. The first + * field should be 8 and the second 16 + * characters or less. + * + * can_stall Set to permit function to halt bulk endpoints. + * Disabled on some USB devices known not + * to work correctly. You should set it + * to true. + * + * If "removable" is not set for a LUN then a backing file must be + * specified. If it is set, then NULL filename means the LUN's medium + * is not loaded (an empty string as "filename" in the fsg_config + * structure causes error). The CD-ROM emulation includes a single + * data track and no audio tracks; hence there need be only one + * backing file per LUN. Note also that the CD-ROM block length is + * set to 512 rather than the more common value 2048. + * + * + * MSF includes support for module parameters. If gadget using it + * decides to use it, the following module parameters will be + * available: + * + * file=filename[,filename...] + * Names of the files or block devices used for + * backing storage. + * ro=b[,b...] Default false, boolean for read-only access. + * removable=b[,b...] + * Default true, boolean for removable media. + * cdrom=b[,b...] Default false, boolean for whether to emulate + * a CD-ROM drive. + * luns=N Default N = number of filenames, number of + * LUNs to support. + * stall Default determined according to the type of + * USB device controller (usually true), + * boolean to permit the driver to halt + * bulk endpoints. + * + * The module parameters may be prefixed with some string. You need + * to consult gadget's documentation or source to verify whether it is + * using those module parameters and if it does what are the prefixes + * (look for FSG_MODULE_PARAMETERS() macro usage, what's inside it is + * the prefix). + * + * + * Requirements are modest; only a bulk-in and a bulk-out endpoint are + * needed. The memory requirement amounts to two 16K buffers, size + * configurable by a parameter. Support is included for both + * full-speed and high-speed operation. + * + * Note that the driver is slightly non-portable in that it assumes a + * single memory/DMA buffer will be useable for bulk-in, bulk-out, and + * interrupt-in endpoints. With most device controllers this isn't an + * issue, but there may be some with hardware restrictions that prevent + * a buffer from being used by more than one endpoint. + * + * + * The pathnames of the backing files and the ro settings are + * available in the attribute files "file" and "ro" in the lun (or + * to be more precise in a directory which name comes from + * "lun_name_format" option!) subdirectory of the gadget's sysfs + * directory. If the "removable" option is set, writing to these + * files will simulate ejecting/loading the medium (writing an empty + * line means eject) and adjusting a write-enable tab. Changes to the + * ro setting are not allowed when the medium is loaded or if CD-ROM + * emulation is being used. + * + * + * This function is heavily based on "File-backed Storage Gadget" by + * Alan Stern which in turn is heavily based on "Gadget Zero" by David + * Brownell. The driver's SCSI command interface was based on the + * "Information technology - Small Computer System Interface - 2" + * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93, + * available at . + * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which + * was based on the "Universal Serial Bus Mass Storage Class UFI + * Command Specification" document, Revision 1.0, December 14, 1998, + * available at + * . + */ + + +/* + * Driver Design + * + * The MSF is fairly straightforward. There is a main kernel + * thread that handles most of the work. Interrupt routines field + * callbacks from the controller driver: bulk- and interrupt-request + * completion notifications, endpoint-0 events, and disconnect events. + * Completion events are passed to the main thread by wakeup calls. Many + * ep0 requests are handled at interrupt time, but SetInterface, + * SetConfiguration, and device reset requests are forwarded to the + * thread in the form of "exceptions" using SIGUSR1 signals (since they + * should interrupt any ongoing file I/O operations). + * + * The thread's main routine implements the standard command/data/status + * parts of a SCSI interaction. It and its subroutines are full of tests + * for pending signals/exceptions -- all this polling is necessary since + * the kernel has no setjmp/longjmp equivalents. (Maybe this is an + * indication that the driver really wants to be running in userspace.) + * An important point is that so long as the thread is alive it keeps an + * open reference to the backing file. This will prevent unmounting + * the backing file's underlying filesystem and could cause problems + * during system shutdown, for example. To prevent such problems, the + * thread catches INT, TERM, and KILL signals and converts them into + * an EXIT exception. + * + * In normal operation the main thread is started during the gadget's + * fsg_bind() callback and stopped during fsg_unbind(). But it can + * also exit when it receives a signal, and there's no point leaving + * the gadget running when the thread is dead. At of this moment, MSF + * provides no way to deregister the gadget when thread dies -- maybe + * a callback functions is needed. + * + * To provide maximum throughput, the driver uses a circular pipeline of + * buffer heads (struct fsg_buffhd). In principle the pipeline can be + * arbitrarily long; in practice the benefits don't justify having more + * than 2 stages (i.e., double buffering). But it helps to think of the + * pipeline as being a long one. Each buffer head contains a bulk-in and + * a bulk-out request pointer (since the buffer can be used for both + * output and input -- directions always are given from the host's + * point of view) as well as a pointer to the buffer and various state + * variables. + * + * Use of the pipeline follows a simple protocol. There is a variable + * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. + * At any time that buffer head may still be in use from an earlier + * request, so each buffer head has a state variable indicating whether + * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the + * buffer head to be EMPTY, filling the buffer either by file I/O or by + * USB I/O (during which the buffer head is BUSY), and marking the buffer + * head FULL when the I/O is complete. Then the buffer will be emptied + * (again possibly by USB I/O, during which it is marked BUSY) and + * finally marked EMPTY again (possibly by a completion routine). + * + * A module parameter tells the driver to avoid stalling the bulk + * endpoints wherever the transport specification allows. This is + * necessary for some UDCs like the SuperH, which cannot reliably clear a + * halt on a bulk endpoint. However, under certain circumstances the + * Bulk-only specification requires a stall. In such cases the driver + * will halt the endpoint and set a flag indicating that it should clear + * the halt in software during the next device reset. Hopefully this + * will permit everything to work correctly. Furthermore, although the + * specification allows the bulk-out endpoint to halt when the host sends + * too much data, implementing this would cause an unavoidable race. + * The driver will always use the "no-stall" approach for OUT transfers. + * + * One subtle point concerns sending status-stage responses for ep0 + * requests. Some of these requests, such as device reset, can involve + * interrupting an ongoing file I/O operation, which might take an + * arbitrarily long time. During that delay the host might give up on + * the original ep0 request and issue a new one. When that happens the + * driver should not notify the host about completion of the original + * request, as the host will no longer be waiting for it. So the driver + * assigns to each ep0 request a unique tag, and it keeps track of the + * tag value of the request associated with a long-running exception + * (device-reset, interface-change, or configuration-change). When the + * exception handler is finished, the status-stage response is submitted + * only if the current ep0 request tag is equal to the exception request + * tag. Thus only the most recently received ep0 request will get a + * status-stage response. + * + * Warning: This driver source file is too long. It ought to be split up + * into a header file plus about 3 separate .c files, to handle the details + * of the Gadget, USB Mass Storage, and SCSI protocols. + */ + + +/* #define VERBOSE_DEBUG */ +/* #define DUMP_MSGS */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gadget_chips.h" + + + +/*------------------------------------------------------------------------*/ + +#define FSG_DRIVER_DESC "Mass Storage Function" +#define FSG_DRIVER_VERSION "2009/09/11" + +static const char fsg_string_interface[] = "Mass Storage"; + + +#define FSG_NO_INTR_EP 1 +#define FSG_BUFFHD_STATIC_BUFFER 1 +#define FSG_NO_DEVICE_STRINGS 1 +#define FSG_NO_OTG 1 +#define FSG_NO_INTR_EP 1 + +#include "storage_common.c" + + +/*-------------------------------------------------------------------------*/ + +struct fsg_dev; + + +/* Data shared by all the FSG instances. */ +struct fsg_common { + struct usb_gadget *gadget; + struct fsg_dev *fsg; + struct fsg_dev *prev_fsg; + + /* filesem protects: backing files in use */ + struct rw_semaphore filesem; + + /* lock protects: state, all the req_busy's */ + spinlock_t lock; + + struct usb_ep *ep0; /* Copy of gadget->ep0 */ + struct usb_request *ep0req; /* Copy of cdev->req */ + unsigned int ep0_req_tag; + const char *ep0req_name; + + struct fsg_buffhd *next_buffhd_to_fill; + struct fsg_buffhd *next_buffhd_to_drain; + struct fsg_buffhd buffhds[FSG_NUM_BUFFERS]; + + int cmnd_size; + u8 cmnd[MAX_COMMAND_SIZE]; + + unsigned int nluns; + unsigned int lun; + struct fsg_lun *luns; + struct fsg_lun *curlun; + + unsigned int bulk_out_maxpacket; + enum fsg_state state; /* For exception handling */ + unsigned int exception_req_tag; + + u8 config, new_config; + enum data_direction data_dir; + u32 data_size; + u32 data_size_from_cmnd; + u32 tag; + u32 residue; + u32 usb_amount_left; + + unsigned int can_stall:1; + unsigned int free_storage_on_release:1; + unsigned int phase_error:1; + unsigned int short_packet_received:1; + unsigned int bad_lun_okay:1; + unsigned int running:1; + + int thread_wakeup_needed; + struct completion thread_notifier; + struct task_struct *thread_task; + + /* Callback function to call when thread exits. */ + void (*thread_exits)(struct fsg_common *common); + /* Gadget's private data. */ + void *private_data; + + /* Vendor (8 chars), product (16 chars), release (4 + * hexadecimal digits) and NUL byte */ + char inquiry_string[8 + 16 + 4 + 1]; + + struct kref ref; +}; + + +struct fsg_config { + unsigned nluns; + struct fsg_lun_config { + const char *filename; + char ro; + char removable; + char cdrom; + } luns[FSG_MAX_LUNS]; + + const char *lun_name_format; + const char *thread_name; + + /* Callback function to call when thread exits. */ + void (*thread_exits)(struct fsg_common *common); + /* Gadget's private data. */ + void *private_data; + + const char *vendor_name; /* 8 characters or less */ + const char *product_name; /* 16 characters or less */ + u16 release; + + char can_stall; +}; + + +struct fsg_dev { + struct usb_function function; + struct usb_gadget *gadget; /* Copy of cdev->gadget */ + struct fsg_common *common; + + u16 interface_number; + + unsigned int bulk_in_enabled:1; + unsigned int bulk_out_enabled:1; + + unsigned long atomic_bitflags; +#define IGNORE_BULK_OUT 0 + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; +}; + + +static inline int __fsg_is_set(struct fsg_common *common, + const char *func, unsigned line) +{ + if (common->fsg) + return 1; + ERROR(common, "common->fsg is NULL in %s at %u\n", func, line); + return 0; +} + +#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__)) + + +static inline struct fsg_dev *fsg_from_func(struct usb_function *f) +{ + return container_of(f, struct fsg_dev, function); +} + + +typedef void (*fsg_routine_t)(struct fsg_dev *); + +static int exception_in_progress(struct fsg_common *common) +{ + return common->state > FSG_STATE_IDLE; +} + +/* Make bulk-out requests be divisible by the maxpacket size */ +static void set_bulk_out_req_length(struct fsg_common *common, + struct fsg_buffhd *bh, unsigned int length) +{ + unsigned int rem; + + bh->bulk_out_intended_length = length; + rem = length % common->bulk_out_maxpacket; + if (rem > 0) + length += common->bulk_out_maxpacket - rem; + bh->outreq->length = length; +} + +/*-------------------------------------------------------------------------*/ + +static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) +{ + const char *name; + + if (ep == fsg->bulk_in) + name = "bulk-in"; + else if (ep == fsg->bulk_out) + name = "bulk-out"; + else + name = ep->name; + DBG(fsg, "%s set halt\n", name); + return usb_ep_set_halt(ep); +} + + +/*-------------------------------------------------------------------------*/ + +/* These routines may be called in process context or in_irq */ + +/* Caller must hold fsg->lock */ +static void wakeup_thread(struct fsg_common *common) +{ + /* Tell the main thread that something has happened */ + common->thread_wakeup_needed = 1; + if (common->thread_task) + wake_up_process(common->thread_task); +} + + +static void raise_exception(struct fsg_common *common, enum fsg_state new_state) +{ + unsigned long flags; + + /* Do nothing if a higher-priority exception is already in progress. + * If a lower-or-equal priority exception is in progress, preempt it + * and notify the main thread by sending it a signal. */ + spin_lock_irqsave(&common->lock, flags); + if (common->state <= new_state) { + common->exception_req_tag = common->ep0_req_tag; + common->state = new_state; + if (common->thread_task) + send_sig_info(SIGUSR1, SEND_SIG_FORCED, + common->thread_task); + } + spin_unlock_irqrestore(&common->lock, flags); +} + + +/*-------------------------------------------------------------------------*/ + +static int ep0_queue(struct fsg_common *common) +{ + int rc; + + rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC); + common->ep0->driver_data = common; + if (rc != 0 && rc != -ESHUTDOWN) { + /* We can't do much more than wait for a reset */ + WARNING(common, "error in submission: %s --> %d\n", + common->ep0->name, rc); + } + return rc; +} + +/*-------------------------------------------------------------------------*/ + +/* Bulk and interrupt endpoint completion handlers. + * These always run in_irq. */ + +static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + if (req->status || req->actual != req->length) + DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + smp_wmb(); + spin_lock(&common->lock); + bh->inreq_busy = 0; + bh->state = BUF_STATE_EMPTY; + wakeup_thread(common); + spin_unlock(&common->lock); +} + +static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + dump_msg(common, "bulk-out", req->buf, req->actual); + if (req->status || req->actual != bh->bulk_out_intended_length) + DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, + bh->bulk_out_intended_length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Hold the lock while we update the request and buffer states */ + smp_wmb(); + spin_lock(&common->lock); + bh->outreq_busy = 0; + bh->state = BUF_STATE_FULL; + wakeup_thread(common); + spin_unlock(&common->lock); +} + + +/*-------------------------------------------------------------------------*/ + +/* Ep0 class-specific handlers. These always run in_irq. */ + +static int fsg_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_request *req = fsg->common->ep0req; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + if (!fsg->common->config) + return -EOPNOTSUPP; + + switch (ctrl->bRequest) { + + case USB_BULK_RESET_REQUEST: + if (ctrl->bRequestType != + (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0) + return -EDOM; + + /* Raise an exception to stop the current operation + * and reinitialize our state. */ + DBG(fsg, "bulk reset request\n"); + raise_exception(fsg->common, FSG_STATE_RESET); + return DELAYED_STATUS; + + case USB_BULK_GET_MAX_LUN_REQUEST: + if (ctrl->bRequestType != + (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0) + return -EDOM; + VDBG(fsg, "get max LUN\n"); + *(u8 *) req->buf = fsg->common->nluns - 1; + return 1; + } + + VDBG(fsg, + "unknown class-specific control req " + "%02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + le16_to_cpu(ctrl->wValue), w_index, w_length); + return -EOPNOTSUPP; +} + + +/*-------------------------------------------------------------------------*/ + +/* All the following routines run in process context */ + + +/* Use this for bulk or interrupt transfers, not ep0 */ +static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, + struct usb_request *req, int *pbusy, + enum fsg_buffer_state *state) +{ + int rc; + + if (ep == fsg->bulk_in) + dump_msg(fsg, "bulk-in", req->buf, req->length); + + spin_lock_irq(&fsg->common->lock); + *pbusy = 1; + *state = BUF_STATE_BUSY; + spin_unlock_irq(&fsg->common->lock); + rc = usb_ep_queue(ep, req, GFP_KERNEL); + if (rc != 0) { + *pbusy = 0; + *state = BUF_STATE_EMPTY; + + /* We can't do much more than wait for a reset */ + + /* Note: currently the net2280 driver fails zero-length + * submissions if DMA is enabled. */ + if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && + req->length == 0)) + WARNING(fsg, "error in submission: %s --> %d\n", + ep->name, rc); + } +} + +#define START_TRANSFER_OR(common, ep_name, req, pbusy, state) \ + if (fsg_is_set(common)) \ + start_transfer((common)->fsg, (common)->fsg->ep_name, \ + req, pbusy, state); \ + else + +#define START_TRANSFER(common, ep_name, req, pbusy, state) \ + START_TRANSFER_OR(common, ep_name, req, pbusy, state) (void)0 + + + +static int sleep_thread(struct fsg_common *common) +{ + int rc = 0; + + /* Wait until a signal arrives or we are woken up */ + for (;;) { + try_to_freeze(); + set_current_state(TASK_INTERRUPTIBLE); + if (signal_pending(current)) { + rc = -EINTR; + break; + } + if (common->thread_wakeup_needed) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); + common->thread_wakeup_needed = 0; + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int do_read(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + u32 lba; + struct fsg_buffhd *bh; + int rc; + u32 amount_left; + loff_t file_offset, file_offset_tmp; + unsigned int amount; + //unsigned int partial_page; + ssize_t nread; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (common->cmnd[0] == SC_READ_6) + lba = get_unaligned_be24(&common->cmnd[1]); + else { + lba = get_unaligned_be32(&common->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = don't read from the + * cache), but we don't implement them. */ + if ((common->cmnd[1] & ~0x18) != 0) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + file_offset = ((loff_t) lba) << 9; + + /* Carry out the file reads */ + amount_left = common->data_size_from_cmnd; + if (unlikely(amount_left == 0)) + return -EIO; /* No default reply */ + + for (;;) { + + /* Figure out how much we need to read: + * Try to read the remaining amount. + * But don't read more than the buffer size. + * And don't try to read past the end of the file. + * Finally, if we're not at a page boundary, don't read past + * the next page. + * If this means reading 0 then we were asked to read past + * the end of file. */ + amount = min(amount_left, FSG_BUFLEN); + amount = min((loff_t) amount, + curlun->file_length - file_offset); + /* + partial_page = file_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - + partial_page); + */ + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + /* If we were asked to read past the end of file, + * end with an empty buffer. */ + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + bh->inreq->length = 0; + bh->state = BUF_STATE_FULL; + break; + } + + /* Perform the read */ + file_offset_tmp = file_offset; + nread = vfs_read(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nread); + if (signal_pending(current)) + return -EINTR; + + if (nread < 0) { + LDBG(curlun, "error in file read: %d\n", + (int) nread); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file read: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); /* Round down to a block */ + } + file_offset += nread; + amount_left -= nread; + common->residue -= nread; + bh->inreq->length = nread; + bh->state = BUF_STATE_FULL; + + /* If an error occurred, report it and its position */ + if (nread < amount) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + if (amount_left == 0) + break; /* No more left to read */ + + /* Send this buffer and go read some more */ + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + } + + return -EIO; /* No default reply */ +} + + +/*-------------------------------------------------------------------------*/ + +static int do_write(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + u32 lba; + struct fsg_buffhd *bh; + int get_some_more; + u32 amount_left_to_req, amount_left_to_write; + loff_t usb_offset, file_offset, file_offset_tmp; + unsigned int amount; + //unsigned int partial_page; + ssize_t nwritten; + int rc; + + if (curlun->ro) { + curlun->sense_data = SS_WRITE_PROTECTED; + return -EINVAL; + } + spin_lock(&curlun->filp->f_lock); + curlun->filp->f_flags |= O_SYNC; /* Default is to wait */ + spin_unlock(&curlun->filp->f_lock); + + /* Get the starting Logical Block Address and check that it's + * not too big */ + if (common->cmnd[0] == SC_WRITE_6) + lba = get_unaligned_be24(&common->cmnd[1]); + else { + lba = get_unaligned_be32(&common->cmnd[2]); + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) and FUA (Force Unit Access = write directly to the + * medium). We don't implement DPO; we implement FUA by + * performing synchronous output. */ + if (common->cmnd[1] & ~0x18) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (common->cmnd[1] & 0x08) { /* FUA */ + spin_lock(&curlun->filp->f_lock); + curlun->filp->f_flags |= O_SYNC; + spin_unlock(&curlun->filp->f_lock); + } + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* Carry out the file writes */ + get_some_more = 1; + file_offset = usb_offset = ((loff_t) lba) << 9; + amount_left_to_req = common->data_size_from_cmnd; + amount_left_to_write = common->data_size_from_cmnd; + + while (amount_left_to_write > 0) { + + /* Queue a request for more data from the host */ + bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && get_some_more) { + + /* Figure out how much we want to get: + * Try to get the remaining amount. + * But don't get more than the buffer size. + * And don't try to go past the end of the file. + * If we're not at a page boundary, + * don't go past the next page. + * If this means getting 0, then we were asked + * to write past the end of file. + * Finally, round down to a block boundary. */ + amount = min(amount_left_to_req, FSG_BUFLEN); + amount = min((loff_t) amount, curlun->file_length - + usb_offset); + /* + partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); + if (partial_page > 0) + amount = min(amount, + (unsigned int) PAGE_CACHE_SIZE - partial_page); + */ + + if (amount == 0) { + get_some_more = 0; + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = usb_offset >> 9; + curlun->info_valid = 1; + continue; + } + amount -= (amount & 511); + if (amount == 0) { + + /* Why were we were asked to transfer a + * partial block? */ + get_some_more = 0; + continue; + } + + /* Get the next buffer */ + usb_offset += amount; + common->usb_amount_left -= amount; + amount_left_to_req -= amount; + if (amount_left_to_req == 0) + get_some_more = 0; + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + continue; + } + + /* Write the received data to the backing file */ + bh = common->next_buffhd_to_drain; + if (bh->state == BUF_STATE_EMPTY && !get_some_more) + break; /* We stopped early */ + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); + common->next_buffhd_to_drain = bh->next; + bh->state = BUF_STATE_EMPTY; + + /* Did something go wrong with the transfer? */ + if (bh->outreq->status != 0) { + curlun->sense_data = SS_COMMUNICATION_FAILURE; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + amount = bh->outreq->actual; + if (curlun->file_length - file_offset < amount) { + LERROR(curlun, + "write %u @ %llu beyond end %llu\n", + amount, (unsigned long long) file_offset, + (unsigned long long) curlun->file_length); + amount = curlun->file_length - file_offset; + } + + /* Perform the write */ + file_offset_tmp = file_offset; + nwritten = vfs_write(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); + VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nwritten); + if (signal_pending(current)) + return -EINTR; /* Interrupted! */ + + if (nwritten < 0) { + LDBG(curlun, "error in file write: %d\n", + (int) nwritten); + nwritten = 0; + } else if (nwritten < amount) { + LDBG(curlun, "partial file write: %d/%u\n", + (int) nwritten, amount); + nwritten -= (nwritten & 511); + /* Round down to a block */ + } + file_offset += nwritten; + amount_left_to_write -= nwritten; + common->residue -= nwritten; + + /* If an error occurred, report it and its position */ + if (nwritten < amount) { + curlun->sense_data = SS_WRITE_ERROR; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + /* Did the host decide to stop early? */ + if (bh->outreq->actual != bh->outreq->length) { + common->short_packet_received = 1; + break; + } + continue; + } + + /* Wait for something to happen */ + rc = sleep_thread(common); + if (rc) + return rc; + } + + return -EIO; /* No default reply */ +} + + +/*-------------------------------------------------------------------------*/ + +static int do_synchronize_cache(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + int rc; + + /* We ignore the requested LBA and write out all file's + * dirty data buffers. */ + rc = fsg_lun_fsync_sub(curlun); + if (rc) + curlun->sense_data = SS_WRITE_ERROR; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static void invalidate_sub(struct fsg_lun *curlun) +{ + struct file *filp = curlun->filp; + struct inode *inode = filp->f_path.dentry->d_inode; + unsigned long rc; + + rc = invalidate_mapping_pages(inode->i_mapping, 0, -1); + VLDBG(curlun, "invalidate_inode_pages -> %ld\n", rc); +} + +static int do_verify(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + u32 lba; + u32 verification_length; + struct fsg_buffhd *bh = common->next_buffhd_to_fill; + loff_t file_offset, file_offset_tmp; + u32 amount_left; + unsigned int amount; + ssize_t nread; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + lba = get_unaligned_be32(&common->cmnd[2]); + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + /* We allow DPO (Disable Page Out = don't save data in the + * cache) but we don't implement it. */ + if (common->cmnd[1] & ~0x10) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + verification_length = get_unaligned_be16(&common->cmnd[7]); + if (unlikely(verification_length == 0)) + return -EIO; /* No default reply */ + + /* Prepare to carry out the file verify */ + amount_left = verification_length << 9; + file_offset = ((loff_t) lba) << 9; + + /* Write out all the dirty buffers before invalidating them */ + fsg_lun_fsync_sub(curlun); + if (signal_pending(current)) + return -EINTR; + + invalidate_sub(curlun); + if (signal_pending(current)) + return -EINTR; + + /* Just try to read the requested blocks */ + while (amount_left > 0) { + + /* Figure out how much we need to read: + * Try to read the remaining amount, but not more than + * the buffer size. + * And don't try to read past the end of the file. + * If this means reading 0 then we were asked to read + * past the end of file. */ + amount = min(amount_left, FSG_BUFLEN); + amount = min((loff_t) amount, + curlun->file_length - file_offset); + if (amount == 0) { + curlun->sense_data = + SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + + /* Perform the read */ + file_offset_tmp = file_offset; + nread = vfs_read(curlun->filp, + (char __user *) bh->buf, + amount, &file_offset_tmp); + VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, + (unsigned long long) file_offset, + (int) nread); + if (signal_pending(current)) + return -EINTR; + + if (nread < 0) { + LDBG(curlun, "error in file verify: %d\n", + (int) nread); + nread = 0; + } else if (nread < amount) { + LDBG(curlun, "partial file verify: %d/%u\n", + (int) nread, amount); + nread -= (nread & 511); /* Round down to a sector */ + } + if (nread == 0) { + curlun->sense_data = SS_UNRECOVERED_READ_ERROR; + curlun->sense_data_info = file_offset >> 9; + curlun->info_valid = 1; + break; + } + file_offset += nread; + amount_left -= nread; + } + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + u8 *buf = (u8 *) bh->buf; + + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; + memset(buf, 0, 36); + buf[0] = 0x7f; /* Unsupported, no device-type */ + buf[4] = 31; /* Additional length */ + return 36; + } + + buf[0] = curlun->cdrom ? TYPE_CDROM : TYPE_DISK; + buf[1] = curlun->removable ? 0x80 : 0; + buf[2] = 2; /* ANSI SCSI level 2 */ + buf[3] = 2; /* SCSI-2 INQUIRY data format */ + buf[4] = 31; /* Additional length */ + buf[5] = 0; /* No special options */ + buf[6] = 0; + buf[7] = 0; + memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string); + return 36; +} + + +static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + u8 *buf = (u8 *) bh->buf; + u32 sd, sdinfo; + int valid; + + /* + * From the SCSI-2 spec., section 7.9 (Unit attention condition): + * + * If a REQUEST SENSE command is received from an initiator + * with a pending unit attention condition (before the target + * generates the contingent allegiance condition), then the + * target shall either: + * a) report any pending sense data and preserve the unit + * attention condition on the logical unit, or, + * b) report the unit attention condition, may discard any + * pending sense data, and clear the unit attention + * condition on the logical unit for that initiator. + * + * FSG normally uses option a); enable this code to use option b). + */ +#if 0 + if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + } +#endif + + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + sdinfo = 0; + valid = 0; + } else { + sd = curlun->sense_data; + sdinfo = curlun->sense_data_info; + valid = curlun->info_valid << 7; + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + + memset(buf, 0, 18); + buf[0] = valid | 0x70; /* Valid, current error */ + buf[2] = SK(sd); + put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */ + buf[7] = 18 - 8; /* Additional sense length */ + buf[12] = ASC(sd); + buf[13] = ASCQ(sd); + return 18; +} + + +static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + u32 lba = get_unaligned_be32(&common->cmnd[2]); + int pmi = common->cmnd[8]; + u8 *buf = (u8 *) bh->buf; + + /* Check the PMI and LBA fields */ + if (pmi > 1 || (pmi == 0 && lba != 0)) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + put_unaligned_be32(curlun->num_sectors - 1, &buf[0]); + /* Max logical block */ + put_unaligned_be32(512, &buf[4]); /* Block length */ + return 8; +} + + +static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + int msf = common->cmnd[1] & 0x02; + u32 lba = get_unaligned_be32(&common->cmnd[2]); + u8 *buf = (u8 *) bh->buf; + + if (common->cmnd[1] & ~0x02) { /* Mask away MSF */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + if (lba >= curlun->num_sectors) { + curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + return -EINVAL; + } + + memset(buf, 0, 8); + buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */ + store_cdrom_address(&buf[4], msf, lba); + return 8; +} + + +static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + int msf = common->cmnd[1] & 0x02; + int start_track = common->cmnd[6]; + u8 *buf = (u8 *) bh->buf; + + if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ + start_track > 1) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + memset(buf, 0, 20); + buf[1] = (20-2); /* TOC data length */ + buf[2] = 1; /* First track number */ + buf[3] = 1; /* Last track number */ + buf[5] = 0x16; /* Data track, copying allowed */ + buf[6] = 0x01; /* Only track is number 1 */ + store_cdrom_address(&buf[8], msf, 0); + + buf[13] = 0x16; /* Lead-out track is data */ + buf[14] = 0xAA; /* Lead-out track number */ + store_cdrom_address(&buf[16], msf, curlun->num_sectors); + return 20; +} + + +static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + int mscmnd = common->cmnd[0]; + u8 *buf = (u8 *) bh->buf; + u8 *buf0 = buf; + int pc, page_code; + int changeable_values, all_pages; + int valid_page = 0; + int len, limit; + + if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + pc = common->cmnd[2] >> 6; + page_code = common->cmnd[2] & 0x3f; + if (pc == 3) { + curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; + return -EINVAL; + } + changeable_values = (pc == 1); + all_pages = (page_code == 0x3f); + + /* Write the mode parameter header. Fixed values are: default + * medium type, no cache control (DPOFUA), and no block descriptors. + * The only variable value is the WriteProtect bit. We will fill in + * the mode data length later. */ + memset(buf, 0, 8); + if (mscmnd == SC_MODE_SENSE_6) { + buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 4; + limit = 255; + } else { /* SC_MODE_SENSE_10 */ + buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 8; + limit = 65535; /* Should really be FSG_BUFLEN */ + } + + /* No block descriptors */ + + /* The mode pages, in numerical order. The only page we support + * is the Caching page. */ + if (page_code == 0x08 || all_pages) { + valid_page = 1; + buf[0] = 0x08; /* Page code */ + buf[1] = 10; /* Page length */ + memset(buf+2, 0, 10); /* None of the fields are changeable */ + + if (!changeable_values) { + buf[2] = 0x04; /* Write cache enable, */ + /* Read cache not disabled */ + /* No cache retention priorities */ + put_unaligned_be16(0xffff, &buf[4]); + /* Don't disable prefetch */ + /* Minimum prefetch = 0 */ + put_unaligned_be16(0xffff, &buf[8]); + /* Maximum prefetch */ + put_unaligned_be16(0xffff, &buf[10]); + /* Maximum prefetch ceiling */ + } + buf += 12; + } + + /* Check that a valid page was requested and the mode data length + * isn't too long. */ + len = buf - buf0; + if (!valid_page || len > limit) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + /* Store the mode data length */ + if (mscmnd == SC_MODE_SENSE_6) + buf0[0] = len - 1; + else + put_unaligned_be16(len - 2, buf0); + return len; +} + + +static int do_start_stop(struct fsg_common *common) +{ + if (!common->curlun) { + return -EINVAL; + } else if (!common->curlun->removable) { + common->curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + return 0; +} + + +static int do_prevent_allow(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + int prevent; + + if (!common->curlun) { + return -EINVAL; + } else if (!common->curlun->removable) { + common->curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + prevent = common->cmnd[4] & 0x01; + if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + if (curlun->prevent_medium_removal && !prevent) + fsg_lun_fsync_sub(curlun); + curlun->prevent_medium_removal = prevent; + return 0; +} + + +static int do_read_format_capacities(struct fsg_common *common, + struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + u8 *buf = (u8 *) bh->buf; + + buf[0] = buf[1] = buf[2] = 0; + buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */ + buf += 4; + + put_unaligned_be32(curlun->num_sectors, &buf[0]); + /* Number of blocks */ + put_unaligned_be32(512, &buf[4]); /* Block length */ + buf[4] = 0x02; /* Current capacity */ + return 12; +} + + +static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + + /* We don't support MODE SELECT */ + if (curlun) + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; +} + + +/*-------------------------------------------------------------------------*/ + +static int halt_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + rc = fsg_set_halt(fsg, fsg->bulk_in); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint halt\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); + rc = 0; + break; + } + + /* Wait for a short time and then try again */ + if (msleep_interruptible(100) != 0) + return -EINTR; + rc = usb_ep_set_halt(fsg->bulk_in); + } + return rc; +} + +static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + DBG(fsg, "bulk-in set wedge\n"); + rc = usb_ep_set_wedge(fsg->bulk_in); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint wedge\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); + rc = 0; + break; + } + + /* Wait for a short time and then try again */ + if (msleep_interruptible(100) != 0) + return -EINTR; + rc = usb_ep_set_wedge(fsg->bulk_in); + } + return rc; +} + +static int pad_with_zeros(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill; + u32 nkeep = bh->inreq->length; + u32 nsend; + int rc; + + bh->state = BUF_STATE_EMPTY; /* For the first iteration */ + fsg->common->usb_amount_left = nkeep + fsg->common->residue; + while (fsg->common->usb_amount_left > 0) { + + /* Wait for the next buffer to be free */ + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg->common); + if (rc) + return rc; + } + + nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN); + memset(bh->buf + nkeep, 0, nsend - nkeep); + bh->inreq->length = nsend; + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + bh = fsg->common->next_buffhd_to_fill = bh->next; + fsg->common->usb_amount_left -= nsend; + nkeep = 0; + } + return 0; +} + +static int throw_away_data(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + u32 amount; + int rc; + + for (bh = common->next_buffhd_to_drain; + bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0; + bh = common->next_buffhd_to_drain) { + + /* Throw away the data in a filled buffer */ + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); + bh->state = BUF_STATE_EMPTY; + common->next_buffhd_to_drain = bh->next; + + /* A short packet or an error ends everything */ + if (bh->outreq->actual != bh->outreq->length || + bh->outreq->status != 0) { + raise_exception(common, + FSG_STATE_ABORT_BULK_OUT); + return -EINTR; + } + continue; + } + + /* Try to submit another request if we need one */ + bh = common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY + && common->usb_amount_left > 0) { + amount = min(common->usb_amount_left, FSG_BUFLEN); + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = amount; + bh->bulk_out_intended_length = amount; + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh->next; + common->usb_amount_left -= amount; + continue; + } + + /* Otherwise wait for something to happen */ + rc = sleep_thread(common); + if (rc) + return rc; + } + return 0; +} + + +static int finish_reply(struct fsg_common *common) +{ + struct fsg_buffhd *bh = common->next_buffhd_to_fill; + int rc = 0; + + switch (common->data_dir) { + case DATA_DIR_NONE: + break; /* Nothing to send */ + + /* If we don't know whether the host wants to read or write, + * this must be CB or CBI with an unknown command. We mustn't + * try to send or receive any data. So stall both bulk pipes + * if we can and wait for a reset. */ + case DATA_DIR_UNKNOWN: + if (!common->can_stall) { + /* Nothing */ + } else if (fsg_is_set(common)) { + fsg_set_halt(common->fsg, common->fsg->bulk_out); + rc = halt_bulk_in_endpoint(common->fsg); + } else { + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; + } + break; + + /* All but the last buffer of data must have already been sent */ + case DATA_DIR_TO_HOST: + if (common->data_size == 0) { + /* Nothing to send */ + + /* If there's no residue, simply send the last buffer */ + } else if (common->residue == 0) { + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + return -EIO; + common->next_buffhd_to_fill = bh->next; + + /* For Bulk-only, if we're allowed to stall then send the + * short packet and halt the bulk-in endpoint. If we can't + * stall, pad out the remaining data with 0's. */ + } else if (common->can_stall) { + bh->inreq->zero = 1; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if + * common->fsg is NULL */ + rc = -EIO; + common->next_buffhd_to_fill = bh->next; + if (common->fsg) + rc = halt_bulk_in_endpoint(common->fsg); + } else if (fsg_is_set(common)) { + rc = pad_with_zeros(common->fsg); + } else { + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; + } + break; + + /* We have processed all we want from the data the host has sent. + * There may still be outstanding bulk-out requests. */ + case DATA_DIR_FROM_HOST: + if (common->residue == 0) { + /* Nothing to receive */ + + /* Did the host stop sending unexpectedly early? */ + } else if (common->short_packet_received) { + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; + + /* We haven't processed all the incoming data. Even though + * we may be allowed to stall, doing so would cause a race. + * The controller may already have ACK'ed all the remaining + * bulk-out packets, in which case the host wouldn't see a + * STALL. Not realizing the endpoint was halted, it wouldn't + * clear the halt -- leading to problems later on. */ +#if 0 + } else if (common->can_stall) { + if (fsg_is_set(common)) + fsg_set_halt(common->fsg, + common->fsg->bulk_out); + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; +#endif + + /* We can't stall. Read in the excess data and throw it + * all away. */ + } else { + rc = throw_away_data(common); + } + break; + } + return rc; +} + + +static int send_status(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + struct fsg_buffhd *bh; + struct bulk_cs_wrap *csw; + int rc; + u8 status = USB_STATUS_PASS; + u32 sd, sdinfo = 0; + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + if (curlun) { + sd = curlun->sense_data; + sdinfo = curlun->sense_data_info; + } else if (common->bad_lun_okay) + sd = SS_NO_SENSE; + else + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + + if (common->phase_error) { + DBG(common, "sending phase-error status\n"); + status = USB_STATUS_PHASE_ERROR; + sd = SS_INVALID_COMMAND; + } else if (sd != SS_NO_SENSE) { + DBG(common, "sending command-failure status\n"); + status = USB_STATUS_FAIL; + VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" + " info x%x\n", + SK(sd), ASC(sd), ASCQ(sd), sdinfo); + } + + /* Store and send the Bulk-only CSW */ + csw = (void *)bh->buf; + + csw->Signature = cpu_to_le32(USB_BULK_CS_SIG); + csw->Tag = common->tag; + csw->Residue = cpu_to_le32(common->residue); + csw->Status = status; + + bh->inreq->length = USB_BULK_CS_WRAP_LEN; + bh->inreq->zero = 0; + START_TRANSFER_OR(common, bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + + common->next_buffhd_to_fill = bh->next; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* Check whether the command is properly formed and whether its data size + * and direction agree with the values we already have. */ +static int check_command(struct fsg_common *common, int cmnd_size, + enum data_direction data_dir, unsigned int mask, + int needs_medium, const char *name) +{ + int i; + int lun = common->cmnd[1] >> 5; + static const char dirletter[4] = {'u', 'o', 'i', 'n'}; + char hdlen[20]; + struct fsg_lun *curlun; + + hdlen[0] = 0; + if (common->data_dir != DATA_DIR_UNKNOWN) + sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir], + common->data_size); + VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", + name, cmnd_size, dirletter[(int) data_dir], + common->data_size_from_cmnd, common->cmnd_size, hdlen); + + /* We can't reply at all until we know the correct data direction + * and size. */ + if (common->data_size_from_cmnd == 0) + data_dir = DATA_DIR_NONE; + if (common->data_size < common->data_size_from_cmnd) { + /* Host data size < Device data size is a phase error. + * Carry out the command, but only transfer as much as + * we are allowed. */ + common->data_size_from_cmnd = common->data_size; + common->phase_error = 1; + } + common->residue = common->data_size; + common->usb_amount_left = common->data_size; + + /* Conflicting data directions is a phase error */ + if (common->data_dir != data_dir + && common->data_size_from_cmnd > 0) { + common->phase_error = 1; + return -EINVAL; + } + + /* Verify the length of the command itself */ + if (cmnd_size != common->cmnd_size) { + + /* Special case workaround: There are plenty of buggy SCSI + * implementations. Many have issues with cbw->Length + * field passing a wrong command size. For those cases we + * always try to work around the problem by using the length + * sent by the host side provided it is at least as large + * as the correct command length. + * Examples of such cases would be MS-Windows, which issues + * REQUEST SENSE with cbw->Length == 12 where it should + * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and + * REQUEST SENSE with cbw->Length == 10 where it should + * be 6 as well. + */ + if (cmnd_size <= common->cmnd_size) { + DBG(common, "%s is buggy! Expected length %d " + "but we got %d\n", name, + cmnd_size, common->cmnd_size); + cmnd_size = common->cmnd_size; + } else { + common->phase_error = 1; + return -EINVAL; + } + } + + /* Check that the LUN values are consistent */ + if (common->lun != lun) + DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n", + common->lun, lun); + + /* Check the LUN */ + if (common->lun >= 0 && common->lun < common->nluns) { + curlun = &common->luns[common->lun]; + common->curlun = curlun; + if (common->cmnd[0] != SC_REQUEST_SENSE) { + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + } else { + common->curlun = NULL; + curlun = NULL; + common->bad_lun_okay = 0; + + /* INQUIRY and REQUEST SENSE commands are explicitly allowed + * to use unsupported LUNs; all others may not. */ + if (common->cmnd[0] != SC_INQUIRY && + common->cmnd[0] != SC_REQUEST_SENSE) { + DBG(common, "unsupported LUN %d\n", common->lun); + return -EINVAL; + } + } + + /* If a unit attention condition exists, only INQUIRY and + * REQUEST SENSE commands are allowed; anything else must fail. */ + if (curlun && curlun->unit_attention_data != SS_NO_SENSE && + common->cmnd[0] != SC_INQUIRY && + common->cmnd[0] != SC_REQUEST_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + return -EINVAL; + } + + /* Check that only command bytes listed in the mask are non-zero */ + common->cmnd[1] &= 0x1f; /* Mask away the LUN */ + for (i = 1; i < cmnd_size; ++i) { + if (common->cmnd[i] && !(mask & (1 << i))) { + if (curlun) + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + + /* If the medium isn't mounted and the command needs to access + * it, return an error. */ + if (curlun && !fsg_lun_is_open(curlun) && needs_medium) { + curlun->sense_data = SS_MEDIUM_NOT_PRESENT; + return -EINVAL; + } + + return 0; +} + + +static int do_scsi_command(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + int rc; + int reply = -EINVAL; + int i; + static char unknown[16]; + + dump_cdb(common); + + /* Wait for the next buffer to become available for data or status */ + bh = common->next_buffhd_to_fill; + common->next_buffhd_to_drain = bh; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + common->phase_error = 0; + common->short_packet_received = 0; + + down_read(&common->filesem); /* We're using the backing file */ + switch (common->cmnd[0]) { + + case SC_INQUIRY: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "INQUIRY"); + if (reply == 0) + reply = do_inquiry(common, bh); + break; + + case SC_MODE_SELECT_6: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (1<<1) | (1<<4), 0, + "MODE SELECT(6)"); + if (reply == 0) + reply = do_mode_select(common, bh); + break; + + case SC_MODE_SELECT_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (3<<7), 0, + "MODE SELECT(10)"); + if (reply == 0) + reply = do_mode_select(common, bh); + break; + + case SC_MODE_SENSE_6: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (1<<4), 0, + "MODE SENSE(6)"); + if (reply == 0) + reply = do_mode_sense(common, bh); + break; + + case SC_MODE_SENSE_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (3<<7), 0, + "MODE SENSE(10)"); + if (reply == 0) + reply = do_mode_sense(common, bh); + break; + + case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<4), 0, + "PREVENT-ALLOW MEDIUM REMOVAL"); + if (reply == 0) + reply = do_prevent_allow(common); + break; + + case SC_READ_6: + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (7<<1) | (1<<4), 1, + "READ(6)"); + if (reply == 0) + reply = do_read(common); + break; + + case SC_READ_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]) << 9; + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "READ(10)"); + if (reply == 0) + reply = do_read(common); + break; + + case SC_READ_12: + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]) << 9; + reply = check_command(common, 12, DATA_DIR_TO_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "READ(12)"); + if (reply == 0) + reply = do_read(common); + break; + + case SC_READ_CAPACITY: + common->data_size_from_cmnd = 8; + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (0xf<<2) | (1<<8), 1, + "READ CAPACITY"); + if (reply == 0) + reply = do_read_capacity(common, bh); + break; + + case SC_READ_HEADER: + if (!common->curlun || !common->curlun->cdrom) + goto unknown_cmnd; + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7) | (0x1f<<1), 1, + "READ HEADER"); + if (reply == 0) + reply = do_read_header(common, bh); + break; + + case SC_READ_TOC: + if (!common->curlun || !common->curlun->cdrom) + goto unknown_cmnd; + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (7<<6) | (1<<1), 1, + "READ TOC"); + if (reply == 0) + reply = do_read_toc(common, bh); + break; + + case SC_READ_FORMAT_CAPACITIES: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (3<<7), 1, + "READ FORMAT CAPACITIES"); + if (reply == 0) + reply = do_read_format_capacities(common, bh); + break; + + case SC_REQUEST_SENSE: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "REQUEST SENSE"); + if (reply == 0) + reply = do_request_sense(common, bh); + break; + + case SC_START_STOP_UNIT: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<1) | (1<<4), 0, + "START-STOP UNIT"); + if (reply == 0) + reply = do_start_stop(common); + break; + + case SC_SYNCHRONIZE_CACHE: + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (0xf<<2) | (3<<7), 1, + "SYNCHRONIZE CACHE"); + if (reply == 0) + reply = do_synchronize_cache(common); + break; + + case SC_TEST_UNIT_READY: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + 0, 1, + "TEST UNIT READY"); + break; + + /* Although optional, this command is used by MS-Windows. We + * support a minimal version: BytChk must be 0. */ + case SC_VERIFY: + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (1<<1) | (0xf<<2) | (3<<7), 1, + "VERIFY"); + if (reply == 0) + reply = do_verify(common); + break; + + case SC_WRITE_6: + i = common->cmnd[4]; + common->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (7<<1) | (1<<4), 1, + "WRITE(6)"); + if (reply == 0) + reply = do_write(common); + break; + + case SC_WRITE_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]) << 9; + reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (3<<7), 1, + "WRITE(10)"); + if (reply == 0) + reply = do_write(common); + break; + + case SC_WRITE_12: + common->data_size_from_cmnd = + get_unaligned_be32(&common->cmnd[6]) << 9; + reply = check_command(common, 12, DATA_DIR_FROM_HOST, + (1<<1) | (0xf<<2) | (0xf<<6), 1, + "WRITE(12)"); + if (reply == 0) + reply = do_write(common); + break; + + /* Some mandatory commands that we recognize but don't implement. + * They don't mean much in this setting. It's left as an exercise + * for anyone interested to implement RESERVE and RELEASE in terms + * of Posix locks. */ + case SC_FORMAT_UNIT: + case SC_RELEASE: + case SC_RESERVE: + case SC_SEND_DIAGNOSTIC: + /* Fall through */ + + default: +unknown_cmnd: + common->data_size_from_cmnd = 0; + sprintf(unknown, "Unknown x%02x", common->cmnd[0]); + reply = check_command(common, common->cmnd_size, + DATA_DIR_UNKNOWN, 0xff, 0, unknown); + if (reply == 0) { + common->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } + break; + } + up_read(&common->filesem); + + if (reply == -EINTR || signal_pending(current)) + return -EINTR; + + /* Set up the single reply buffer for finish_reply() */ + if (reply == -EINVAL) + reply = 0; /* Error reply length */ + if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { + reply = min((u32) reply, common->data_size_from_cmnd); + bh->inreq->length = reply; + bh->state = BUF_STATE_FULL; + common->residue -= reply; + } /* Otherwise it's already set */ + + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct usb_request *req = bh->outreq; + struct fsg_bulk_cb_wrap *cbw = req->buf; + struct fsg_common *common = fsg->common; + + /* Was this a real packet? Should it be ignored? */ + if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) + return -EINVAL; + + /* Is the CBW valid? */ + if (req->actual != USB_BULK_CB_WRAP_LEN || + cbw->Signature != cpu_to_le32( + USB_BULK_CB_SIG)) { + DBG(fsg, "invalid CBW: len %u sig 0x%x\n", + req->actual, + le32_to_cpu(cbw->Signature)); + + /* The Bulk-only spec says we MUST stall the IN endpoint + * (6.6.1), so it's unavoidable. It also says we must + * retain this state until the next reset, but there's + * no way to tell the controller driver it should ignore + * Clear-Feature(HALT) requests. + * + * We aren't required to halt the OUT endpoint; instead + * we can simply accept and discard any data received + * until the next reset. */ + wedge_bulk_in_endpoint(fsg); + set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + return -EINVAL; + } + + /* Is the CBW meaningful? */ + if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || + cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) { + DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " + "cmdlen %u\n", + cbw->Lun, cbw->Flags, cbw->Length); + + /* We can do anything we want here, so let's stall the + * bulk pipes if we are allowed to. */ + if (common->can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + halt_bulk_in_endpoint(fsg); + } + return -EINVAL; + } + + /* Save the command for later */ + common->cmnd_size = cbw->Length; + memcpy(common->cmnd, cbw->CDB, common->cmnd_size); + if (cbw->Flags & USB_BULK_IN_FLAG) + common->data_dir = DATA_DIR_TO_HOST; + else + common->data_dir = DATA_DIR_FROM_HOST; + common->data_size = le32_to_cpu(cbw->DataTransferLength); + if (common->data_size == 0) + common->data_dir = DATA_DIR_NONE; + common->lun = cbw->Lun; + common->tag = cbw->Tag; + return 0; +} + + +static int get_next_command(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + int rc = 0; + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(common); + if (rc) + return rc; + } + + /* Queue a request to read a Bulk-only CBW */ + set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN); + bh->outreq->short_not_ok = 1; + START_TRANSFER_OR(common, bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + + /* We will drain the buffer in software, which means we + * can reuse it for the next filling. No need to advance + * next_buffhd_to_fill. */ + + /* Wait for the CBW to arrive */ + while (bh->state != BUF_STATE_FULL) { + rc = sleep_thread(common); + if (rc) + return rc; + } + smp_rmb(); + rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO; + bh->state = BUF_STATE_EMPTY; + + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int enable_endpoint(struct fsg_common *common, struct usb_ep *ep, + const struct usb_endpoint_descriptor *d) +{ + int rc; + + ep->driver_data = common; + rc = usb_ep_enable(ep, d); + if (rc) + ERROR(common, "can't enable %s, result %d\n", ep->name, rc); + return rc; +} + +static int alloc_request(struct fsg_common *common, struct usb_ep *ep, + struct usb_request **preq) +{ + *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (*preq) + return 0; + ERROR(common, "can't allocate request for %s\n", ep->name); + return -ENOMEM; +} + +/* + * Reset interface setting and re-init endpoint state (toggle etc). + * Call with altsetting < 0 to disable the interface. The only other + * available altsetting is 0, which enables the interface. + */ +static int do_set_interface(struct fsg_common *common, int altsetting) +{ + int rc = 0; + int i; + const struct usb_endpoint_descriptor *d; + + if (common->running) + DBG(common, "reset interface\n"); + +reset: + /* Deallocate the requests */ + if (common->prev_fsg) { + struct fsg_dev *fsg = common->prev_fsg; + + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + if (bh->inreq) { + usb_ep_free_request(fsg->bulk_in, bh->inreq); + bh->inreq = NULL; + } + if (bh->outreq) { + usb_ep_free_request(fsg->bulk_out, bh->outreq); + bh->outreq = NULL; + } + } + + /* Disable the endpoints */ + if (fsg->bulk_in_enabled) { + usb_ep_disable(fsg->bulk_in); + fsg->bulk_in_enabled = 0; + } + if (fsg->bulk_out_enabled) { + usb_ep_disable(fsg->bulk_out); + fsg->bulk_out_enabled = 0; + } + + common->prev_fsg = 0; + } + + common->running = 0; + if (altsetting < 0 || rc != 0) + return rc; + + DBG(common, "set interface %d\n", altsetting); + + if (fsg_is_set(common)) { + struct fsg_dev *fsg = common->fsg; + common->prev_fsg = common->fsg; + + /* Enable the endpoints */ + d = fsg_ep_desc(common->gadget, + &fsg_fs_bulk_in_desc, &fsg_hs_bulk_in_desc); + rc = enable_endpoint(common, fsg->bulk_in, d); + if (rc) + goto reset; + fsg->bulk_in_enabled = 1; + + d = fsg_ep_desc(common->gadget, + &fsg_fs_bulk_out_desc, &fsg_hs_bulk_out_desc); + rc = enable_endpoint(common, fsg->bulk_out, d); + if (rc) + goto reset; + fsg->bulk_out_enabled = 1; + common->bulk_out_maxpacket = le16_to_cpu(d->wMaxPacketSize); + clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + + /* Allocate the requests */ + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + rc = alloc_request(common, fsg->bulk_in, &bh->inreq); + if (rc) + goto reset; + rc = alloc_request(common, fsg->bulk_out, &bh->outreq); + if (rc) + goto reset; + bh->inreq->buf = bh->outreq->buf = bh->buf; + bh->inreq->context = bh->outreq->context = bh; + bh->inreq->complete = bulk_in_complete; + bh->outreq->complete = bulk_out_complete; + } + + common->running = 1; + for (i = 0; i < common->nluns; ++i) + common->luns[i].unit_attention_data = SS_RESET_OCCURRED; + return rc; + } else { + return -EIO; + } +} + + +/* + * Change our operational configuration. This code must agree with the code + * that returns config descriptors, and with interface altsetting code. + * + * It's also responsible for power management interactions. Some + * configurations might not work with our current power sources. + * For now we just assume the gadget is always self-powered. + */ +static int do_set_config(struct fsg_common *common, u8 new_config) +{ + int rc = 0; + + /* Disable the single interface */ + if (common->config != 0) { + DBG(common, "reset config\n"); + common->config = 0; + rc = do_set_interface(common, -1); + } + + /* Enable the interface */ + if (new_config != 0) { + common->config = new_config; + rc = do_set_interface(common, 0); + if (rc != 0) + common->config = 0; /* Reset on errors */ + } + return rc; +} + + +/****************************** ALT CONFIGS ******************************/ + + +static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->common->prev_fsg = fsg->common->fsg; + fsg->common->fsg = fsg; + fsg->common->new_config = 1; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); + return 0; +} + +static void fsg_disable(struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + fsg->common->prev_fsg = fsg->common->fsg; + fsg->common->fsg = fsg; + fsg->common->new_config = 0; + raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); +} + + +/*-------------------------------------------------------------------------*/ + +static void handle_exception(struct fsg_common *common) +{ + siginfo_t info; + int sig; + int i; + struct fsg_buffhd *bh; + enum fsg_state old_state; + u8 new_config; + struct fsg_lun *curlun; + unsigned int exception_req_tag; + int rc; + + /* Clear the existing signals. Anything but SIGUSR1 is converted + * into a high-priority EXIT exception. */ + for (;;) { + sig = dequeue_signal_lock(current, ¤t->blocked, &info); + if (!sig) + break; + if (sig != SIGUSR1) { + if (common->state < FSG_STATE_EXIT) + DBG(common, "Main thread exiting on signal\n"); + raise_exception(common, FSG_STATE_EXIT); + } + } + + /* Cancel all the pending transfers */ + if (fsg_is_set(common)) { + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + if (bh->inreq_busy) + usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); + if (bh->outreq_busy) + usb_ep_dequeue(common->fsg->bulk_out, + bh->outreq); + } + + /* Wait until everything is idle */ + for (;;) { + int num_active = 0; + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + num_active += bh->inreq_busy + bh->outreq_busy; + } + if (num_active == 0) + break; + if (sleep_thread(common)) + return; + } + + /* Clear out the controller's fifos */ + if (common->fsg->bulk_in_enabled) + usb_ep_fifo_flush(common->fsg->bulk_in); + if (common->fsg->bulk_out_enabled) + usb_ep_fifo_flush(common->fsg->bulk_out); + } + + /* Reset the I/O buffer states and pointers, the SCSI + * state, and the exception. Then invoke the handler. */ + spin_lock_irq(&common->lock); + + for (i = 0; i < FSG_NUM_BUFFERS; ++i) { + bh = &common->buffhds[i]; + bh->state = BUF_STATE_EMPTY; + } + common->next_buffhd_to_fill = &common->buffhds[0]; + common->next_buffhd_to_drain = &common->buffhds[0]; + exception_req_tag = common->exception_req_tag; + new_config = common->new_config; + old_state = common->state; + + if (old_state == FSG_STATE_ABORT_BULK_OUT) + common->state = FSG_STATE_STATUS_PHASE; + else { + for (i = 0; i < common->nluns; ++i) { + curlun = &common->luns[i]; + curlun->prevent_medium_removal = 0; + curlun->sense_data = SS_NO_SENSE; + curlun->unit_attention_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + common->state = FSG_STATE_IDLE; + } + spin_unlock_irq(&common->lock); + + /* Carry out any extra actions required for the exception */ + switch (old_state) { + case FSG_STATE_ABORT_BULK_OUT: + send_status(common); + spin_lock_irq(&common->lock); + if (common->state == FSG_STATE_STATUS_PHASE) + common->state = FSG_STATE_IDLE; + spin_unlock_irq(&common->lock); + break; + + case FSG_STATE_RESET: + /* In case we were forced against our will to halt a + * bulk endpoint, clear the halt now. (The SuperH UDC + * requires this.) */ + if (!fsg_is_set(common)) + break; + if (test_and_clear_bit(IGNORE_BULK_OUT, + &common->fsg->atomic_bitflags)) + usb_ep_clear_halt(common->fsg->bulk_in); + + if (common->ep0_req_tag == exception_req_tag) + ep0_queue(common); /* Complete the status stage */ + + /* Technically this should go here, but it would only be + * a waste of time. Ditto for the INTERFACE_CHANGE and + * CONFIG_CHANGE cases. */ + /* for (i = 0; i < common->nluns; ++i) */ + /* common->luns[i].unit_attention_data = */ + /* SS_RESET_OCCURRED; */ + break; + + case FSG_STATE_CONFIG_CHANGE: + rc = do_set_config(common, new_config); + if (common->ep0_req_tag != exception_req_tag) + break; + if (rc != 0) { /* STALL on errors */ + DBG(common, "ep0 set halt\n"); + usb_ep_set_halt(common->ep0); + } else { /* Complete the status stage */ + ep0_queue(common); + } + break; + + case FSG_STATE_EXIT: + case FSG_STATE_TERMINATED: + do_set_config(common, 0); /* Free resources */ + spin_lock_irq(&common->lock); + common->state = FSG_STATE_TERMINATED; /* Stop the thread */ + spin_unlock_irq(&common->lock); + break; + + case FSG_STATE_INTERFACE_CHANGE: + case FSG_STATE_DISCONNECT: + case FSG_STATE_COMMAND_PHASE: + case FSG_STATE_DATA_PHASE: + case FSG_STATE_STATUS_PHASE: + case FSG_STATE_IDLE: + break; + } +} + + +/*-------------------------------------------------------------------------*/ + +static int fsg_main_thread(void *common_) +{ + struct fsg_common *common = common_; + + /* Allow the thread to be killed by a signal, but set the signal mask + * to block everything but INT, TERM, KILL, and USR1. */ + allow_signal(SIGINT); + allow_signal(SIGTERM); + allow_signal(SIGKILL); + allow_signal(SIGUSR1); + + /* Allow the thread to be frozen */ + set_freezable(); + + /* Arrange for userspace references to be interpreted as kernel + * pointers. That way we can pass a kernel pointer to a routine + * that expects a __user pointer and it will work okay. */ + set_fs(get_ds()); + + /* The main loop */ + while (common->state != FSG_STATE_TERMINATED) { + if (exception_in_progress(common) || signal_pending(current)) { + handle_exception(common); + continue; + } + + if (!common->running) { + sleep_thread(common); + continue; + } + + if (get_next_command(common)) + continue; + + spin_lock_irq(&common->lock); + if (!exception_in_progress(common)) + common->state = FSG_STATE_DATA_PHASE; + spin_unlock_irq(&common->lock); + + if (do_scsi_command(common) || finish_reply(common)) + continue; + + spin_lock_irq(&common->lock); + if (!exception_in_progress(common)) + common->state = FSG_STATE_STATUS_PHASE; + spin_unlock_irq(&common->lock); + + if (send_status(common)) + continue; + + spin_lock_irq(&common->lock); + if (!exception_in_progress(common)) + common->state = FSG_STATE_IDLE; + spin_unlock_irq(&common->lock); + } + + spin_lock_irq(&common->lock); + common->thread_task = NULL; + spin_unlock_irq(&common->lock); + + if (common->thread_exits) + common->thread_exits(common); + + /* Let the unbind and cleanup routines know the thread has exited */ + complete_and_exit(&common->thread_notifier, 0); +} + + +/*************************** DEVICE ATTRIBUTES ***************************/ + +/* Write permission is checked per LUN in store_*() functions. */ +static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro); +static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file); + +static struct fsg_dev *_fsg; + +static int isActive(void) { + return _fsg->function.config->cdev->deactivations == 0 ? 1 : 0; +} + +static ssize_t fsg_show_active(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", isActive()); +} + +static ssize_t fsg_store_active(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int i; + + if (sscanf(buf, "%d", &i) != 1) + return -EINVAL; + + /* + if (i == 0) { + dUsbExit(); + dUsbInit(); + } + else if (i == 1) { + do_set_config(fsg_common, 0); + } + else if (i == 2) { + raise_exception(fsg_common, FSG_STATE_CONFIG_CHANGE); + } + */ + + if (isActive() != i) { + if (i == 0) { + usb_function_deactivate(&_fsg->function); + } else if (i == 1) { + usb_function_activate(&_fsg->function); + } else { + return -EINVAL; + } + } + + return count; +} + +static DEVICE_ATTR(active, 0644, fsg_show_active, fsg_store_active); + + +/****************************** FSG COMMON ******************************/ + +static void fsg_common_release(struct kref *ref); + +static void fsg_lun_release(struct device *dev) +{ + /* Nothing needs to be done */ +} + +static inline void fsg_common_get(struct fsg_common *common) +{ + kref_get(&common->ref); +} + +static inline void fsg_common_put(struct fsg_common *common) +{ + kref_put(&common->ref, fsg_common_release); +} + + +static struct fsg_common *fsg_common_init(struct fsg_common *common, + struct usb_composite_dev *cdev, + struct fsg_config *cfg) +{ + struct usb_gadget *gadget = cdev->gadget; + struct fsg_buffhd *bh; + struct fsg_lun *curlun; + struct fsg_lun_config *lcfg; + int nluns, i, rc; + char *pathbuf; + + /* Find out how many LUNs there should be */ + nluns = cfg->nluns; + if (nluns < 1 || nluns > FSG_MAX_LUNS) { + dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns); + return ERR_PTR(-EINVAL); + } + + /* Allocate? */ + if (!common) { + common = kzalloc(sizeof *common, GFP_KERNEL); + if (!common) + return ERR_PTR(-ENOMEM); + common->free_storage_on_release = 1; + } else { + memset(common, 0, sizeof common); + common->free_storage_on_release = 0; + } + + common->private_data = cfg->private_data; + + common->gadget = gadget; + common->ep0 = gadget->ep0; + common->ep0req = cdev->req; + + /* Maybe allocate device-global string IDs, and patch descriptors */ + if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { + rc = usb_string_id(cdev); + if (rc < 0) { + kfree(common); + return ERR_PTR(rc); + } + fsg_strings[FSG_STRING_INTERFACE].id = rc; + fsg_intf_desc.iInterface = rc; + } + + /* Create the LUNs, open their backing files, and register the + * LUN devices in sysfs. */ + curlun = kzalloc(nluns * sizeof *curlun, GFP_KERNEL); + if (!curlun) { + kfree(common); + return ERR_PTR(-ENOMEM); + } + common->luns = curlun; + + init_rwsem(&common->filesem); + + for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) { + curlun->cdrom = !!lcfg->cdrom; + curlun->ro = lcfg->cdrom || lcfg->ro; + curlun->removable = lcfg->removable; + curlun->dev.release = fsg_lun_release; + curlun->dev.parent = &gadget->dev; + /* curlun->dev.driver = &fsg_driver.driver; XXX */ + dev_set_drvdata(&curlun->dev, &common->filesem); + dev_set_name(&curlun->dev, + cfg->lun_name_format + ? cfg->lun_name_format + : "lun%d", + i); + + rc = device_register(&curlun->dev); + if (rc) { + INFO(common, "failed to register LUN%d: %d\n", i, rc); + common->nluns = i; + goto error_release; + } + + rc = device_create_file(&curlun->dev, &dev_attr_ro); + if (rc) + goto error_luns; + rc = device_create_file(&curlun->dev, &dev_attr_file); + if (rc) + goto error_luns; + + rc = device_create_file(&curlun->dev, &dev_attr_active); + if (rc) + goto error_luns; + + if (lcfg->filename) { + rc = fsg_lun_open(curlun, lcfg->filename); + if (rc) + goto error_luns; + } else if (!curlun->removable) { + ERROR(common, "no file given for LUN%d\n", i); + rc = -EINVAL; + goto error_luns; + } + } + common->nluns = nluns; + + + /* Data buffers cyclic list */ + /* Buffers in buffhds are static -- no need for additional + * allocation. */ + bh = common->buffhds; + i = FSG_NUM_BUFFERS - 1; + do { + bh->next = bh + 1; + } while (++bh, --i); + bh->next = common->buffhds; + + + /* Prepare inquiryString */ + if (cfg->release != 0xffff) { + i = cfg->release; + } else { + /* The sa1100 controller is not supported */ + i = gadget_is_sa1100(gadget) + ? -1 + : usb_gadget_controller_number(gadget); + if (i >= 0) { + i = 0x0300 + i; + } else { + WARNING(common, "controller '%s' not recognized\n", + gadget->name); + i = 0x0399; + } + } +#define OR(x, y) ((x) ? (x) : (y)) + snprintf(common->inquiry_string, sizeof common->inquiry_string, + "%-8s%-16s%04x", + OR(cfg->vendor_name, "Linux "), + /* Assume product name dependent on the first LUN */ + OR(cfg->product_name, common->luns->cdrom + ? "File-Stor Gadget" + : "File-CD Gadget "), + i); + + + /* Some peripheral controllers are known not to be able to + * halt bulk endpoints correctly. If one of them is present, + * disable stalls. + */ + common->can_stall = cfg->can_stall && + !(gadget_is_sh(common->gadget) || + gadget_is_at91(common->gadget)); + + + spin_lock_init(&common->lock); + kref_init(&common->ref); + + + /* Tell the thread to start working */ + common->thread_exits = cfg->thread_exits; + common->thread_task = + kthread_create(fsg_main_thread, common, + OR(cfg->thread_name, "file-storage")); + if (IS_ERR(common->thread_task)) { + rc = PTR_ERR(common->thread_task); + goto error_release; + } + init_completion(&common->thread_notifier); +#undef OR + + + /* Information */ + INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); + INFO(common, "Number of LUNs=%d\n", common->nluns); + + pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); + for (i = 0, nluns = common->nluns, curlun = common->luns; + i < nluns; + ++curlun, ++i) { + char *p = "(no medium)"; + if (fsg_lun_is_open(curlun)) { + p = "(error)"; + if (pathbuf) { + p = d_path(&curlun->filp->f_path, + pathbuf, PATH_MAX); + if (IS_ERR(p)) + p = "(error)"; + } + } + LINFO(curlun, "LUN: %s%s%sfile: %s\n", + curlun->removable ? "removable " : "", + curlun->ro ? "read only " : "", + curlun->cdrom ? "CD-ROM " : "", + p); + } + kfree(pathbuf); + + DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task)); + + wake_up_process(common->thread_task); + + return common; + + +error_luns: + common->nluns = i + 1; +error_release: + common->state = FSG_STATE_TERMINATED; /* The thread is dead */ + /* Call fsg_common_release() directly, ref might be not + * initialised */ + fsg_common_release(&common->ref); + complete(&common->thread_notifier); + return ERR_PTR(rc); +} + + +static void fsg_common_release(struct kref *ref) +{ + struct fsg_common *common = + container_of(ref, struct fsg_common, ref); + unsigned i = common->nluns; + struct fsg_lun *lun = common->luns; + + /* If the thread isn't already dead, tell it to exit now */ + if (common->state != FSG_STATE_TERMINATED) { + raise_exception(common, FSG_STATE_EXIT); + wait_for_completion(&common->thread_notifier); + + /* The cleanup routine waits for this completion also */ + complete(&common->thread_notifier); + } + + /* Beware tempting for -> do-while optimization: when in error + * recovery nluns may be zero. */ + + for (; i; --i, ++lun) { + device_remove_file(&lun->dev, &dev_attr_ro); + device_remove_file(&lun->dev, &dev_attr_file); + fsg_lun_close(lun); + device_unregister(&lun->dev); + } + + kfree(common->luns); + if (common->free_storage_on_release) + kfree(common); +} + + +/*-------------------------------------------------------------------------*/ + + +static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + + DBG(fsg, "unbind\n"); + fsg_common_put(fsg->common); + kfree(fsg); +} + + +static int fsg_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_gadget *gadget = c->cdev->gadget; + int rc; + int i; + struct usb_ep *ep; + + fsg->gadget = gadget; + + /* New interface */ + i = usb_interface_id(c, f); + if (i < 0) + return i; + fsg_intf_desc.bInterfaceNumber = i; + fsg->interface_number = i; + + /* Find all the endpoints we will use */ + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg->common; /* claim the endpoint */ + fsg->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg->common; /* claim the endpoint */ + fsg->bulk_out = ep; + + if (gadget_is_dualspeed(gadget)) { + /* Assume endpoint addresses are the same for both speeds */ + fsg_hs_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_hs_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + f->hs_descriptors = fsg_hs_function; + } + + return 0; + +autoconf_fail: + ERROR(fsg, "unable to autoconfigure all endpoints\n"); + rc = -ENOTSUPP; + fsg_unbind(c, f); + return rc; +} + + +/****************************** ADD FUNCTION ******************************/ + +static struct usb_gadget_strings *fsg_strings_array[] = { + &fsg_stringtab, + NULL, +}; + +static int fsg_add(struct usb_composite_dev *cdev, + struct usb_configuration *c, + struct fsg_common *common) +{ + struct fsg_dev *fsg; + int rc; + + fsg = kzalloc(sizeof *fsg, GFP_KERNEL); + _fsg = fsg; + if (unlikely(!fsg)) + return -ENOMEM; + + fsg->function.name = FSG_DRIVER_DESC; + fsg->function.strings = fsg_strings_array; + fsg->function.descriptors = fsg_fs_function; + fsg->function.bind = fsg_bind; + fsg->function.unbind = fsg_unbind; + fsg->function.setup = fsg_setup; + fsg->function.set_alt = fsg_set_alt; + fsg->function.disable = fsg_disable; + + fsg->common = common; + /* Our caller holds a reference to common structure so we + * don't have to be worry about it being freed until we return + * from this function. So instead of incrementing counter now + * and decrement in error recovery we increment it only when + * call to usb_add_function() was successful. */ + + rc = usb_add_function(c, &fsg->function); + + if (likely(rc == 0)) + fsg_common_get(fsg->common); + else + kfree(fsg); + + return rc; +} + + + +/************************* Module parameters *************************/ + + +struct fsg_module_parameters { + char *file[FSG_MAX_LUNS]; + int ro[FSG_MAX_LUNS]; + int removable[FSG_MAX_LUNS]; + int cdrom[FSG_MAX_LUNS]; + + unsigned int file_count, ro_count, removable_count, cdrom_count; + unsigned int luns; /* nluns */ + int stall; /* can_stall */ +}; + + +#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \ + module_param_array_named(prefix ## name, params.name, type, \ + &prefix ## params.name ## _count, \ + S_IRUGO); \ + MODULE_PARM_DESC(prefix ## name, desc) + +#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \ + module_param_named(prefix ## name, params.name, type, \ + S_IRUGO); \ + MODULE_PARM_DESC(prefix ## name, desc) + +#define FSG_MODULE_PARAMETERS(prefix, params) \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \ + "names of backing files or devices"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \ + "true to force read-only"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \ + "true to simulate removable media"); \ + _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \ + "true to simulate CD-ROM instead of disk"); \ + _FSG_MODULE_PARAM(prefix, params, luns, uint, \ + "number of LUNs"); \ + _FSG_MODULE_PARAM(prefix, params, stall, bool, \ + "false to prevent bulk stalls") + + +static void +fsg_config_from_params(struct fsg_config *cfg, + const struct fsg_module_parameters *params) +{ + struct fsg_lun_config *lun; + unsigned i; + + /* Configure LUNs */ + cfg->nluns = + min(params->luns ?: (params->file_count ?: 1u), + (unsigned)FSG_MAX_LUNS); + for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) { + lun->ro = !!params->ro[i]; + lun->cdrom = !!params->cdrom[i]; + lun->removable = /* Removable by default */ + params->removable_count <= i || params->removable[i]; + lun->filename = + params->file_count > i && params->file[i][0] + ? params->file[i] + : 0; + } + + /* Let MSF use defaults */ + cfg->lun_name_format = 0; + cfg->thread_name = 0; + cfg->vendor_name = 0; + cfg->product_name = 0; + cfg->release = 0xffff; + + cfg->thread_exits = 0; + cfg->private_data = 0; + + /* Finalise */ + cfg->can_stall = params->stall; +} + +static inline struct fsg_common * +fsg_common_from_params(struct fsg_common *common, + struct usb_composite_dev *cdev, + const struct fsg_module_parameters *params) + __attribute__((unused)); +static inline struct fsg_common * +fsg_common_from_params(struct fsg_common *common, + struct usb_composite_dev *cdev, + const struct fsg_module_parameters *params) +{ + struct fsg_config cfg; + fsg_config_from_params(&cfg, params); + return fsg_common_init(common, cdev, &cfg); +} + diff --git a/brick/kernel/gadget_chips.h b/brick/kernel/gadget_chips.h new file mode 100644 index 00000000..f2d270b2 --- /dev/null +++ b/brick/kernel/gadget_chips.h @@ -0,0 +1,276 @@ +/* + * USB device controllers have lots of quirks. Use these macros in + * gadget drivers or other code that needs to deal with them, and which + * autoconfigures instead of using early binding to the hardware. + * + * This SHOULD eventually work like the ARM mach_is_*() stuff, driven by + * some config file that gets updated as new hardware is supported. + * (And avoiding all runtime comparisons in typical one-choice configs!) + * + * NOTE: some of these controller drivers may not be available yet. + * Some are available on 2.4 kernels; several are available, but not + * yet pushed in the 2.6 mainline tree. + */ + +#ifndef __GADGET_CHIPS_H +#define __GADGET_CHIPS_H + +#ifdef CONFIG_USB_GADGET_NET2280 +#define gadget_is_net2280(g) !strcmp("net2280", (g)->name) +#else +#define gadget_is_net2280(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_AMD5536UDC +#define gadget_is_amd5536udc(g) !strcmp("amd5536udc", (g)->name) +#else +#define gadget_is_amd5536udc(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_DUMMY_HCD +#define gadget_is_dummy(g) !strcmp("dummy_udc", (g)->name) +#else +#define gadget_is_dummy(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_PXA25X +#define gadget_is_pxa(g) !strcmp("pxa25x_udc", (g)->name) +#else +#define gadget_is_pxa(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_GOKU +#define gadget_is_goku(g) !strcmp("goku_udc", (g)->name) +#else +#define gadget_is_goku(g) 0 +#endif + +/* SH3 UDC -- not yet ported 2.4 --> 2.6 */ +#ifdef CONFIG_USB_GADGET_SUPERH +#define gadget_is_sh(g) !strcmp("sh_udc", (g)->name) +#else +#define gadget_is_sh(g) 0 +#endif + +/* not yet stable on 2.6 (would help "original Zaurus") */ +#ifdef CONFIG_USB_GADGET_SA1100 +#define gadget_is_sa1100(g) !strcmp("sa1100_udc", (g)->name) +#else +#define gadget_is_sa1100(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_LH7A40X +#define gadget_is_lh7a40x(g) !strcmp("lh7a40x_udc", (g)->name) +#else +#define gadget_is_lh7a40x(g) 0 +#endif + +/* handhelds.org tree (?) */ +#ifdef CONFIG_USB_GADGET_MQ11XX +#define gadget_is_mq11xx(g) !strcmp("mq11xx_udc", (g)->name) +#else +#define gadget_is_mq11xx(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_OMAP +#define gadget_is_omap(g) !strcmp("omap_udc", (g)->name) +#else +#define gadget_is_omap(g) 0 +#endif + +/* not yet ported 2.4 --> 2.6 */ +#ifdef CONFIG_USB_GADGET_N9604 +#define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name) +#else +#define gadget_is_n9604(g) 0 +#endif + +/* various unstable versions available */ +#ifdef CONFIG_USB_GADGET_PXA27X +#define gadget_is_pxa27x(g) !strcmp("pxa27x_udc", (g)->name) +#else +#define gadget_is_pxa27x(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_ATMEL_USBA +#define gadget_is_atmel_usba(g) !strcmp("atmel_usba_udc", (g)->name) +#else +#define gadget_is_atmel_usba(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_S3C2410 +#define gadget_is_s3c2410(g) !strcmp("s3c2410_udc", (g)->name) +#else +#define gadget_is_s3c2410(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_AT91 +#define gadget_is_at91(g) !strcmp("at91_udc", (g)->name) +#else +#define gadget_is_at91(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_IMX +#define gadget_is_imx(g) !strcmp("imx_udc", (g)->name) +#else +#define gadget_is_imx(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_FSL_USB2 +#define gadget_is_fsl_usb2(g) !strcmp("fsl-usb2-udc", (g)->name) +#else +#define gadget_is_fsl_usb2(g) 0 +#endif + +/* Mentor high speed function controller */ +/* from Montavista kernel (?) */ +#ifdef CONFIG_USB_GADGET_MUSBHSFC +#define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name) +#else +#define gadget_is_musbhsfc(g) 0 +#endif + +/* Mentor high speed "dual role" controller, in peripheral role */ +#ifdef CONFIG_USB_GADGET_MUSB_HDRC +#define gadget_is_musbhdrc(g) !strcmp("musb_hdrc", (g)->name) +#else +#define gadget_is_musbhdrc(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_LANGWELL +#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name)) +#else +#define gadget_is_langwell(g) 0 +#endif + +/* from Montavista kernel (?) */ +#ifdef CONFIG_USB_GADGET_MPC8272 +#define gadget_is_mpc8272(g) !strcmp("mpc8272_udc", (g)->name) +#else +#define gadget_is_mpc8272(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_M66592 +#define gadget_is_m66592(g) !strcmp("m66592_udc", (g)->name) +#else +#define gadget_is_m66592(g) 0 +#endif + +/* Freescale CPM/QE UDC SUPPORT */ +#ifdef CONFIG_USB_GADGET_FSL_QE +#define gadget_is_fsl_qe(g) !strcmp("fsl_qe_udc", (g)->name) +#else +#define gadget_is_fsl_qe(g) 0 +#endif + +#ifdef CONFIG_USB_GADGET_CI13XXX +#define gadget_is_ci13xxx(g) (!strcmp("ci13xxx_udc", (g)->name)) +#else +#define gadget_is_ci13xxx(g) 0 +#endif + +// CONFIG_USB_GADGET_SX2 +// CONFIG_USB_GADGET_AU1X00 +// ... + +#ifdef CONFIG_USB_GADGET_R8A66597 +#define gadget_is_r8a66597(g) !strcmp("r8a66597_udc", (g)->name) +#else +#define gadget_is_r8a66597(g) 0 +#endif + + +/** + * usb_gadget_controller_number - support bcdDevice id convention + * @gadget: the controller being driven + * + * Return a 2-digit BCD value associated with the peripheral controller, + * suitable for use as part of a bcdDevice value, or a negative error code. + * + * NOTE: this convention is purely optional, and has no meaning in terms of + * any USB specification. If you want to use a different convention in your + * gadget driver firmware -- maybe a more formal revision ID -- feel free. + * + * Hosts see these bcdDevice numbers, and are allowed (but not encouraged!) + * to change their behavior accordingly. For example it might help avoiding + * some chip bug. + */ +static inline int usb_gadget_controller_number(struct usb_gadget *gadget) +{ + if (gadget_is_net2280(gadget)) + return 0x01; + else if (gadget_is_dummy(gadget)) + return 0x02; + else if (gadget_is_pxa(gadget)) + return 0x03; + else if (gadget_is_sh(gadget)) + return 0x04; + else if (gadget_is_sa1100(gadget)) + return 0x05; + else if (gadget_is_goku(gadget)) + return 0x06; + else if (gadget_is_mq11xx(gadget)) + return 0x07; + else if (gadget_is_omap(gadget)) + return 0x08; + else if (gadget_is_lh7a40x(gadget)) + return 0x09; + else if (gadget_is_n9604(gadget)) + return 0x10; + else if (gadget_is_pxa27x(gadget)) + return 0x11; + else if (gadget_is_s3c2410(gadget)) + return 0x12; + else if (gadget_is_at91(gadget)) + return 0x13; + else if (gadget_is_imx(gadget)) + return 0x14; + else if (gadget_is_musbhsfc(gadget)) + return 0x15; + else if (gadget_is_musbhdrc(gadget)) + return 0x16; + else if (gadget_is_mpc8272(gadget)) + return 0x17; + else if (gadget_is_atmel_usba(gadget)) + return 0x18; + else if (gadget_is_fsl_usb2(gadget)) + return 0x19; + else if (gadget_is_amd5536udc(gadget)) + return 0x20; + else if (gadget_is_m66592(gadget)) + return 0x21; + else if (gadget_is_fsl_qe(gadget)) + return 0x22; + else if (gadget_is_ci13xxx(gadget)) + return 0x23; + else if (gadget_is_langwell(gadget)) + return 0x24; + else if (gadget_is_r8a66597(gadget)) + return 0x25; + return -ENOENT; +} + + +/** + * gadget_supports_altsettings - return true if altsettings work + * @gadget: the gadget in question + */ +static inline bool gadget_supports_altsettings(struct usb_gadget *gadget) +{ + /* PXA 21x/25x/26x has no altsettings at all */ + if (gadget_is_pxa(gadget)) + return false; + + /* PXA 27x and 3xx have *broken* altsetting support */ + if (gadget_is_pxa27x(gadget)) + return false; + + /* SH3 hardware just doesn't do altsettings */ + if (gadget_is_sh(gadget)) + return false; + + /* Everything else is *presumably* fine ... */ + return true; +} + +#endif /* __GADGET_CHIPS_H */ diff --git a/brick/kernel/source/am1808.h b/brick/kernel/source/am1808.h new file mode 100755 index 00000000..8047520b --- /dev/null +++ b/brick/kernel/source/am1808.h @@ -0,0 +1,249 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef AM1808_H_ +#define AM1808_H_ + +#ifndef PCASM +#include +#else +#define __iomem +#endif + +enum +{ + GP0_0,GP0_1,GP0_2,GP0_3,GP0_4,GP0_5,GP0_6,GP0_7,GP0_8,GP0_9,GP0_10,GP0_11,GP0_12,GP0_13,GP0_14,GP0_15, + GP1_0,GP1_1,GP1_2,GP1_3,GP1_4,GP1_5,GP1_6,GP1_7,GP1_8,GP1_9,GP1_10,GP1_11,GP1_12,GP1_13,GP1_14,GP1_15, + GP2_0,GP2_1,GP2_2,GP2_3,GP2_4,GP2_5,GP2_6,GP2_7,GP2_8,GP2_9,GP2_10,GP2_11,GP2_12,GP2_13,GP2_14,GP2_15, + GP3_0,GP3_1,GP3_2,GP3_3,GP3_4,GP3_5,GP3_6,GP3_7,GP3_8,GP3_9,GP3_10,GP3_11,GP3_12,GP3_13,GP3_14,GP3_15, + GP4_0,GP4_1,GP4_2,GP4_3,GP4_4,GP4_5,GP4_6,GP4_7,GP4_8,GP4_9,GP4_10,GP4_11,GP4_12,GP4_13,GP4_14,GP4_15, + GP5_0,GP5_1,GP5_2,GP5_3,GP5_4,GP5_5,GP5_6,GP5_7,GP5_8,GP5_9,GP5_10,GP5_11,GP5_12,GP5_13,GP5_14,GP5_15, + GP6_0,GP6_1,GP6_2,GP6_3,GP6_4,GP6_5,GP6_6,GP6_7,GP6_8,GP6_9,GP6_10,GP6_11,GP6_12,GP6_13,GP6_14,GP6_15, + GP7_0,GP7_1,GP7_2,GP7_3,GP7_4,GP7_5,GP7_6,GP7_7,GP7_8,GP7_9,GP7_10,GP7_11,GP7_12,GP7_13,GP7_14,GP7_15, + GP8_0,GP8_1,GP8_2,GP8_3,GP8_4,GP8_5,GP8_6,GP8_7,GP8_8,GP8_9,GP8_10,GP8_11,GP8_12,GP8_13,GP8_14,GP8_15, + NO_OF_GPIOS, + UART0_TXD,UART0_RXD,UART1_TXD,UART1_RXD, + SPI0_MOSI,SPI0_MISO,SPI0_SCL,SPI0_CS, + SPI1_MOSI,SPI1_MISO,SPI1_SCL,SPI1_CS, + EPWM1A,EPWM1B,APWM0,APWM1,EPWM0B,AXR3,AXR4 +}; + + +typedef struct +{ + int Pin; + u16 MuxReg; + u32 Mask; + u32 Mode; +} +MRM; + +MRM MuxRegMap[] = +{ // Pin MuxReg Mask Mode + + { GP0_1 , 1, 0xF0FFFFFF, 0x08000000 }, + { GP0_2 , 1, 0xFF0FFFFF, 0x00800000 }, + { GP0_3 , 1, 0xFFF0FFFF, 0x00080000 }, + { GP0_4 , 1, 0xFFFF0FFF, 0x00008000 }, + { GP0_5 , 1, 0xFFFFF0FF, 0x00000800 }, + { GP0_6 , 1, 0xFFFFFF0F, 0x00000080 }, + { GP0_7 , 1, 0xFFFFFFF0, 0x00000008 }, + + { GP0_11, 0, 0xFFF0FFFF, 0x00080000 }, + { GP0_12, 0, 0xFFFF0FFF, 0x00008000 }, + { GP0_13, 0, 0xFFFFF0FF, 0x00000800 }, + { GP0_14, 0, 0xFFFFFF0F, 0x00000080 }, + { GP0_15, 0, 0xFFFFFFF0, 0x00000008 }, + + { GP1_0 , 4, 0x0FFFFFFF, 0x80000000 }, + { GP1_8 , 3, 0xFFFFFFF0, 0x00000004 }, + + { GP1_9, 2, 0xF0FFFFFF, 0x04000000 }, + { GP1_10, 2, 0xFF0FFFFF, 0x00400000 }, + { GP1_11, 2, 0xFFF0FFFF, 0x00040000 }, + { GP1_12, 2, 0xFFFF0FFF, 0x00004000 }, + { GP1_13, 2, 0xFFFFF0FF, 0x00000400 }, + { GP1_14, 2, 0xFFFFFF0F, 0x00000040 }, + { GP1_15, 2, 0xFFFFFFF0, 0x00000008 }, + + { GP2_0, 6, 0x0FFFFFFF, 0x80000000 }, + { GP2_1, 6, 0xF0FFFFFF, 0x08000000 }, + { GP2_2, 6, 0xFF0FFFFF, 0x00800000 }, + { GP2_3, 6, 0xFFF0FFFF, 0x00080000 }, + { GP2_4, 6, 0xFFFF0FFF, 0x00008000 }, + { GP2_5, 6, 0xFFFFF0FF, 0x00000800 }, + { GP2_6, 6, 0xFFFFFF0F, 0x00000080 }, + { GP2_7, 6, 0xFFFFFFF0, 0x00000008 }, + + { GP2_8, 5, 0x0FFFFFFF, 0x80000000 }, + { GP2_9, 5, 0xF0FFFFFF, 0x08000000 }, + { GP2_10, 5, 0xFF0FFFFF, 0x00800000 }, + { GP2_11, 5, 0xFFF0FFFF, 0x00080000 }, + { GP2_12, 5, 0xFFFF0FFF, 0x00008000 }, + { GP2_13, 5, 0xFFFFF0FF, 0x00000800 }, + + { GP3_0, 8, 0x0FFFFFFF, 0x80000000 }, + { GP3_1 , 8, 0xF0FFFFFF, 0x08000000 }, + { GP3_2, 8, 0xFF0FFFFF, 0x00800000 }, + { GP3_3, 8, 0xFFF0FFFF, 0x00080000 }, + { GP3_4, 8, 0xFFFF0FFF, 0x00008000 }, + { GP3_5, 8, 0xFFFFF0FF, 0x00000800 }, + { GP3_6, 8, 0xFFFFFF0F, 0x00000080 }, + { GP3_7, 8, 0xFFFFFFF0, 0x00000008 }, + + { GP3_8, 7, 0x0FFFFFFF, 0x80000000 }, + { GP3_9, 7, 0xF0FFFFFF, 0x08000000 }, + { GP3_10, 7, 0xFF0FFFFF, 0x00800000 }, + { GP3_11, 7, 0xFFF0FFFF, 0x00080000 }, + { GP3_12, 7, 0xFFFF0FFF, 0x00008000 }, + { GP3_13, 7, 0xFFFFF0FF, 0x00000800 }, + { GP3_14, 7, 0xFFFFFF0F, 0x00000080 }, + { GP3_15, 7, 0xFFFFFFF0, 0x00000008 }, + + { GP4_1, 10, 0xF0FFFFFF, 0x08000000 }, + + { GP4_8, 9, 0x0FFFFFFF, 0x80000000 }, + { GP4_9, 9, 0xF0FFFFFF, 0x08000000 }, + { GP4_10, 9, 0xFF0FFFFF, 0x00800000 }, + + { GP4_12, 9, 0xFFFF0FFF, 0x00008000 }, + + { GP4_14, 9, 0xFFFFFF0F, 0x00000080 }, + + { GP5_0, 12, 0x0FFFFFFF, 0x80000000 }, + { GP5_1, 12, 0xF0FFFFFF, 0x08000000 }, + { GP5_2, 12, 0xFF0FFFFF, 0x00800000 }, + { GP5_3, 12, 0xFFF0FFFF, 0x00080000 }, + { GP5_4, 12, 0xFFFF0FFF, 0x00008000 }, + { GP5_5, 12, 0xFFFFF0FF, 0x00000800 }, + { GP5_6, 12, 0xFFFFFF0F, 0x00000080 }, + { GP5_7, 12, 0xFFFFFFF0, 0x00000008 }, + + { GP5_8, 11, 0x0FFFFFFF, 0x80000000 }, + { GP5_9, 11, 0xF0FFFFFF, 0x08000000 }, + { GP5_10, 11, 0xFF0FFFFF, 0x00800000 }, + { GP5_11, 11, 0xFFF0FFFF, 0x00080000 }, + { GP5_12, 11, 0xFFFF0FFF, 0x00008000 }, + { GP5_13, 11, 0xFFFFF0FF, 0x00000800 }, + { GP5_14, 11, 0xFFFFFF0F, 0x00000080 }, + { GP5_15, 11, 0xFFFFFFF0, 0x00000008 }, + + { GP6_0 , 19, 0xF0FFFFFF, 0x08000000 }, + { GP6_1, 19, 0xFF0FFFFF, 0x00800000 }, + { GP6_2, 19, 0xFFF0FFFF, 0x00080000 }, + { GP6_3, 19, 0xFFFF0FFF, 0x00008000 }, + { GP6_4, 19, 0xFFFFF0FF, 0x00000800 }, + { GP6_5, 16, 0xFFFFFF0F, 0x00000080 }, + + { GP6_6, 14, 0xFFFFFF0F, 0x00000080 }, + { GP6_7, 14, 0xFFFFFFF0, 0x00000008 }, + + { GP6_8, 13, 0x0FFFFFFF, 0x80000000 }, + { GP6_9, 13, 0xF0FFFFFF, 0x08000000 }, + { GP6_10, 13, 0xFF0FFFFF, 0x00800000 }, + { GP6_11, 13, 0xFFF0FFFF, 0x00080000 }, + { GP6_12, 13, 0xFFFF0FFF, 0x00008000 }, + { GP6_13, 13, 0xFFFFF0FF, 0x00000800 }, + { GP6_14, 13, 0xFFFFFF0F, 0x00000080 }, + { GP6_15, 13, 0xFFFFFFF0, 0x00000008 }, + + { GP7_4, 17, 0xFF0FFFFF, 0x00800000 }, + { GP7_8, 17, 0xFFFFFF0F, 0x00000080 }, + { GP7_9, 17, 0xFFFFFFF0, 0x00000008 }, + { GP7_10, 16, 0x0FFFFFFF, 0x80000000 }, + { GP7_11, 16, 0xF0FFFFFF, 0x08000000 }, + { GP7_12, 16, 0xFF0FFFFF, 0x00800000 }, + { GP7_13, 16, 0xFFF0FFFF, 0x00080000 }, + { GP7_14, 16, 0xFFFF0FFF, 0x00008000 }, + { GP7_15, 16, 0xFFFFF0FF, 0x00000800 }, + + { GP8_2 , 3 , 0xF0FFFFFF, 0x04000000 }, + { GP8_3 , 3 , 0xFF0FFFFF, 0x00400000 }, + { GP8_5 , 3 , 0xFFFF0FFF, 0x00004000 }, + { GP8_6 , 3 , 0xFFFFF0FF, 0x00000400 }, + { GP8_8 , 19, 0xFFFFFF0F, 0x00000080 }, + { GP8_9 , 19, 0xFFFFFFF0, 0x00000008 }, + { GP8_10, 18, 0x0FFFFFFF, 0x80000000 }, + { GP8_11, 18, 0xF0FFFFFF, 0x08000000 }, + { GP8_12, 18, 0xFF0FFFFF, 0x00800000 }, + { GP8_13, 18, 0xFFF0FFFF, 0x00080000 }, + { GP8_14, 18, 0xFFFF0FFF, 0x00008000 }, + { GP8_15, 18, 0xFFFFF0FF, 0x00000800 }, + + + { UART0_TXD, 3, 0xFF0FFFFF, 0x00200000 }, + { UART0_RXD, 3, 0xFFF0FFFF, 0x00020000 }, + + { UART1_TXD, 4, 0x0FFFFFFF, 0x20000000 }, + { UART1_RXD, 4, 0xF0FFFFFF, 0x02000000 }, + + { SPI0_MOSI, 3, 0xFFFF0FFF, 0x00001000 }, + { SPI0_MISO, 3, 0xFFFFF0FF, 0x00000100 }, + { SPI0_SCL, 3, 0xFFFFFFF0, 0x00000001 }, + { SPI0_CS, 3, 0xF0FFFFFF, 0x01000000 }, + + { SPI1_MOSI, 5, 0xFF0FFFFF, 0x00100000 }, + { SPI1_MISO, 5, 0xFFF0FFFF, 0x00010000 }, + { SPI1_SCL, 5, 0xFFFFF0FF, 0x00000100 }, + { SPI1_CS, 5, 0xFFFF0FFF, 0x00008000 }, + + { EPWM1A, 5, 0xFFFFFFF0, 0x00000002 }, + { EPWM1B, 5, 0xFFFFFF0F, 0x00000020 }, + { APWM0, 2, 0x0FFFFFFF, 0x20000000 }, + { APWM1, 1, 0x0FFFFFFF, 0x40000000 }, + { EPWM0B, 3, 0xFFFFFF0F, 0x00000020 }, + + { AXR3, 2, 0xFFF0FFFF, 0x00010000 }, + { AXR4, 2, 0xFFFF0FFF, 0x00001000 }, + + {-1 } +}; + +typedef struct gpio_controller *__iomem GPIOC; + +typedef struct +{ + int Pin; // GPIO pin number + GPIOC pGpio; // GPIO bank base address + u32 Mask; // GPIO pin mask +} +INPIN; + +#define REGUnlock {\ + iowrite32(0x83E70B13,da8xx_syscfg0_base + 0x38);\ + iowrite32(0x95A4F1E0,da8xx_syscfg0_base + 0x3C);\ + } + +#define REGLock {\ + iowrite32(0x00000000,da8xx_syscfg0_base + 0x38);\ + iowrite32(0x00000000,da8xx_syscfg0_base + 0x3C);\ + } + + + + +#else + +extern MRM MuxRegMap[]; + +#endif /* AM1808_H_ */ + + diff --git a/brick/kernel/source/bytecodes.h b/brick/kernel/source/bytecodes.h new file mode 100644 index 00000000..af1640c8 --- /dev/null +++ b/brick/kernel/source/bytecodes.h @@ -0,0 +1,1744 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception, if other files instantiate templates or use macros or + * inline functions from this file, or you compile this file and link it with + * other works to produce a work based on this file, this file does not by itself + * cause the resulting work to be covered by the GNU General Public License. + * However the source code for this file must still be made available in accordance + * with section (3) of the GNU General Public License. + * + */ + + +#ifndef BYTECODES_H_ +#define BYTECODES_H_ + +#define BYTECODE_VERSION 1.09 + +/*! \page system + * + * \verbatim + */ + +// HARDWARE + +#define vmOUTPUTS 4 //!< Number of output ports in the system +#define vmINPUTS 4 //!< Number of input ports in the system +#define vmBUTTONS 6 //!< Number of buttons in the system +#define vmLEDS 4 //!< Number of LEDs in the system + +#define vmLCD_WIDTH 178 //!< LCD horizontal pixels +#define vmLCD_HEIGHT 128 //!< LCD vertical pixels +#define vmTOPLINE_HEIGHT 10 //!< Top line vertical pixels +#define vmLCD_STORE_LEVELS 3 //!< Store levels + +#define vmDEFAULT_VOLUME 100 +#define vmDEFAULT_SLEEPMINUTES 30 + +// SOFTWARE + +#define vmFG_COLOR 1 //!< Forground color +#define vmBG_COLOR 0 //!< Background color + +#define vmCHAIN_DEPT 4 //!< Number of bricks in the USB daisy chain (master + slaves) + +#define FILEPERMISSIONS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) +#define DIRPERMISSIONS (S_IRWXU | S_IRWXG | S_IRWXO) +#define SYSPERMISSIONS (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) + +#define vmPATHSIZE 84 //!< Max path size excluding trailing forward slash including zero termination +#define vmNAMESIZE 32 //!< Max name size including zero termination (must be divideable by 4) +#define vmEXTSIZE 5 //!< Max extension size including dot and zero termination +#define vmFILENAMESIZE 120 //!< Max filename size including path, name, extension and termination (must be divideable by 4) +#define vmMACSIZE 18 //!< Max WIFI MAC size including zero termination +#define vmIPSIZE 16 //!< Max WIFI IP size including zero termination +#define vmBTADRSIZE 13 //!< Max bluetooth address size including zero termination + +#define vmERR_STRING_SIZE 32 // Inclusive zero termination + +#define vmEVENT_BT_PIN 1 +#define vmEVENT_BT_REQ_CONF 2 + +#define vmMAX_VALID_TYPE 101 //!< Highest valid device type + +// FOLDERS + +#define vmMEMORY_FOLDER "/mnt/ramdisk" //!< Folder for non volatile user programs/data +#define vmPROGRAM_FOLDER "../prjs/BrkProg_SAVE" //!< Folder for On Brick Programming programs +#define vmDATALOG_FOLDER "../prjs/BrkDL_SAVE" //!< Folder for On Brick Data log files +#define vmSDCARD_FOLDER "../prjs/SD_Card" //!< Folder for SD card mount +#define vmUSBSTICK_FOLDER "../prjs/USB_Stick" //!< Folder for USB stick mount + +#define vmPRJS_DIR "../prjs" //!< Project folder +#define vmAPPS_DIR "../apps" //!< Apps folder +#define vmTOOLS_DIR "../tools" //!< Tools folder +#define vmTMP_DIR "../tmp" //!< Temporary folder + +#define vmSETTINGS_DIR "../sys/settings" //!< Folder for non volatile settings + +#define vmDIR_DEEPT 127 //!< Max directory items allocated including "." and ".." + +// FILES USED IN APPLICATION + +#define vmLASTRUN_FILE_NAME "lastrun" //!< Last run filename +#define vmCALDATA_FILE_NAME "caldata" //!< Calibration data filename + +// FILES USED IN APPS + +#define vmSLEEP_FILE_NAME "Sleep" //!< File used in "Sleep" app to save status +#define vmVOLUME_FILE_NAME "Volume" //!< File used in "Volume" app to save status +#define vmWIFI_FILE_NAME "WiFi" //!< File used in "WiFi" app to save status +#define vmBLUETOOTH_FILE_NAME "Bluetooth" //!< File used in "Bluetooth" app to save status + +// EXTENSIONS + +#define vmEXT_SOUND ".rsf" //!< Robot Sound File +#define vmEXT_GRAPHICS ".rgf" //!< Robot Graphics File +#define vmEXT_BYTECODE ".rbf" //!< Robot Byte code File +#define vmEXT_TEXT ".rtf" //!< Robot Text File +#define vmEXT_DATALOG ".rdf" //!< Robot Datalog File +#define vmEXT_PROGRAM ".rpf" //!< Robot Program File +#define vmEXT_CONFIG ".rcf" //!< Robot Configuration File +#define vmEXT_ARCHIVE ".raf" //!< Robot Archive File + +// NAME LENGTHs + +#define vmBRICKNAMESIZE 120 //!< Brick name maximal size (including zero termination) +#define vmBTPASSKEYSIZE 7 //!< Bluetooth pass key size (including zero termination) +#define vmWIFIPASSKEYSIZE 33 //!< WiFi pass key size (including zero termination) + +// VALID CHARACTERS + +#define vmCHARSET_NAME 0x01 //!< Character set allowed in brick name and raw filenames +#define vmCHARSET_FILENAME 0x02 //!< Character set allowed in file names +#define vmCHARSET_BTPASSKEY 0x04 //!< Character set allowed in bluetooth pass key +#define vmCHARSET_WIFIPASSKEY 0x08 //!< Character set allowed in WiFi pass key +#define vmCHARSET_WIFISSID 0x10 //!< Character set allowed in WiFi ssid + +// NUMBER LIMITS + +#define vmDATA8_MIN (-127) //!< DATA8 negative limit +#define vmDATA8_MAX (127) //!< DATA8 positive limit +#define vmDATA16_MIN (-32767) //!< DATA16 negative limit +#define vmDATA16_MAX (32767) //!< DATA16 positive limit +#define vmDATA32_MIN (-2147483647) //!< DATA32 negative limit +#define vmDATA32_MAX (2147483647) //!< DATA32 positive limit +#define vmDATAF_MIN (-2147483647) //!< DATAF negative limit +#define vmDATAF_MAX (2147483647) //!< DATAF positive limit + +#define vmDATA8_NAN (0x80) +#define vmDATA16_NAN (0x8000) +#define vmDATA32_NAN (0x80000000) +#define vmDATAF_NAN (0x7FC00000) + +/* \endverbatim */ + +#define vmPULSE_GUI_BACKGROUND 0x01 +#define vmPULSE_BROWSER 0x02 +#define vmPULSE_KEY 0x04 + + + +/*! \page bytecodedef Byte Code Defines + * + * \verbatim + */ +typedef enum +{ +// \endverbatim \ref VM \verbatim +// 0000.... + opERROR = 0x00, // 0000 + opNOP = 0x01, // 0001 + opPROGRAM_STOP = 0x02, // 0010 + opPROGRAM_START = 0x03, // 0011 + opOBJECT_STOP = 0x04, // 0100 + opOBJECT_START = 0x05, // 0101 + opOBJECT_TRIG = 0x06, // 0110 + opOBJECT_WAIT = 0x07, // 0111 + opRETURN = 0x08, // 1000 + opCALL = 0x09, // 1001 + opOBJECT_END = 0x0A, // 1010 + opSLEEP = 0x0B, // 1011 + opPROGRAM_INFO = 0x0C, // 1100 + opLABEL = 0x0D, // 1101 + opPROBE = 0x0E, // 1110 + opDO = 0x0F, // 1111 + +// \endverbatim \ref cMath "MATH" \verbatim +// 0001.... +// ADD 00.. + opADD8 = 0x10, // 00 + opADD16 = 0x11, // 01 + opADD32 = 0x12, // 10 + opADDF = 0x13, // 11 +// SUB 01.. + opSUB8 = 0x14, // 00 + opSUB16 = 0x15, // 01 + opSUB32 = 0x16, // 10 + opSUBF = 0x17, // 11 +// MUL 10.. + opMUL8 = 0x18, // 00 + opMUL16 = 0x19, // 01 + opMUL32 = 0x1A, // 10 + opMULF = 0x1B, // 11 +// DIV 11.. + opDIV8 = 0x1C, // 00 + opDIV16 = 0x1D, // 01 + opDIV32 = 0x1E, // 10 + opDIVF = 0x1F, // 11 + +// \endverbatim \ref Logic "LOGIC" \verbatim +// LOGIC 0010.... +// OR 00.. + opOR8 = 0x20, // 00 + opOR16 = 0x21, // 01 + opOR32 = 0x22, // 10 + +// AND 01.. + opAND8 = 0x24, // 00 + opAND16 = 0x25, // 01 + opAND32 = 0x26, // 10 + +// XOR 10.. + opXOR8 = 0x28, // 00 + opXOR16 = 0x29, // 01 + opXOR32 = 0x2A, // 10 + +// RL 11.. + opRL8 = 0x2C, // 00 + opRL16 = 0x2D, // 01 + opRL32 = 0x2E, // 10 + +// \endverbatim \ref cMove "MOVE" \verbatim + opINIT_BYTES = 0x2F, // 1111 +// MOVE 0011.... +// MOVE8_ 00.. + opMOVE8_8 = 0x30, // 00 + opMOVE8_16 = 0x31, // 01 + opMOVE8_32 = 0x32, // 10 + opMOVE8_F = 0x33, // 11 +// MOVE16_ 01.. + opMOVE16_8 = 0x34, // 00 + opMOVE16_16 = 0x35, // 01 + opMOVE16_32 = 0x36, // 10 + opMOVE16_F = 0x37, // 11 +// MOVE32_ 10.. + opMOVE32_8 = 0x38, // 00 + opMOVE32_16 = 0x39, // 01 + opMOVE32_32 = 0x3A, // 10 + opMOVE32_F = 0x3B, // 11 +// MOVEF_ 11.. + opMOVEF_8 = 0x3C, // 00 + opMOVEF_16 = 0x3D, // 01 + opMOVEF_32 = 0x3E, // 10 + opMOVEF_F = 0x3F, // 11 + +// \endverbatim \ref cBranch "BRANCH" \verbatim +// BRANCH 010000.. + opJR = 0x40, // 00 + opJR_FALSE = 0x41, // 01 + opJR_TRUE = 0x42, // 10 + opJR_NAN = 0x43, // 11 + +// \endverbatim \ref cCompare "COMPARE" \verbatim +// COMPARE 010..... +// CP_LT 001.. + opCP_LT8 = 0x44, // 00 + opCP_LT16 = 0x45, // 01 + opCP_LT32 = 0x46, // 10 + opCP_LTF = 0x47, // 11 +// CP_GT 010.. + opCP_GT8 = 0x48, // 00 + opCP_GT16 = 0x49, // 01 + opCP_GT32 = 0x4A, // 10 + opCP_GTF = 0x4B, // 11 +// CP_EQ 011.. + opCP_EQ8 = 0x4C, // 00 + opCP_EQ16 = 0x4D, // 01 + opCP_EQ32 = 0x4E, // 10 + opCP_EQF = 0x4F, // 11 +// CP_NEQ 100.. + opCP_NEQ8 = 0x50, // 00 + opCP_NEQ16 = 0x51, // 01 + opCP_NEQ32 = 0x52, // 10 + opCP_NEQF = 0x53, // 11 +// CP_LTEQ 101.. + opCP_LTEQ8 = 0x54, // 00 + opCP_LTEQ16 = 0x55, // 01 + opCP_LTEQ32 = 0x56, // 10 + opCP_LTEQF = 0x57, // 11 +// CP_GTEQ 110.. + opCP_GTEQ8 = 0x58, // 00 + opCP_GTEQ16 = 0x59, // 01 + opCP_GTEQ32 = 0x5A, // 10 + opCP_GTEQF = 0x5B, // 11 + +// \endverbatim \ref Select "SELECT" \verbatim +// SELECT 010111.. + opSELECT8 = 0x5C, // 00 + opSELECT16 = 0x5D, // 01 + opSELECT32 = 0x5E, // 10 + opSELECTF = 0x5F, // 11 + + +// \endverbatim \ref VM \verbatim + opSYSTEM = 0x60, + opPORT_CNV_OUTPUT = 0x61, + opPORT_CNV_INPUT = 0x62, + opNOTE_TO_FREQ = 0x63, + +// \endverbatim \ref cBranch "BRANCH" \verbatim +// BRANCH 011000.. + //? 00 + //? 01 + //? 10 + //? 11 +// JR_LT 001.. + opJR_LT8 = 0x64, // 00 + opJR_LT16 = 0x65, // 01 + opJR_LT32 = 0x66, // 10 + opJR_LTF = 0x67, // 11 +// JR_GT 010.. + opJR_GT8 = 0x68, // 00 + opJR_GT16 = 0x69, // 01 + opJR_GT32 = 0x6A, // 10 + opJR_GTF = 0x6B, // 11 +// JR_EQ 011.. + opJR_EQ8 = 0x6C, // 00 + opJR_EQ16 = 0x6D, // 01 + opJR_EQ32 = 0x6E, // 10 + opJR_EQF = 0x6F, // 11 +// JR_NEQ 100.. + opJR_NEQ8 = 0x70, // 00 + opJR_NEQ16 = 0x71, // 01 + opJR_NEQ32 = 0x72, // 10 + opJR_NEQF = 0x73, // 11 +// JR_LTEQ 101.. + opJR_LTEQ8 = 0x74, // 00 + opJR_LTEQ16 = 0x75, // 01 + opJR_LTEQ32 = 0x76, // 10 + opJR_LTEQF = 0x77, // 11 +// JR_GTEQ 110.. + opJR_GTEQ8 = 0x78, // 00 + opJR_GTEQ16 = 0x79, // 01 + opJR_GTEQ32 = 0x7A, // 10 + opJR_GTEQF = 0x7B, // 11 + +// \endverbatim \ref VM \verbatim + opINFO = 0x7C, // 01111100 + opSTRINGS = 0x7D, // 01111101 + opMEMORY_WRITE = 0x7E, // 01111110 + opMEMORY_READ = 0x7F, // 01111111 + +// SYSTEM 1....... + +// \endverbatim \ref cUi "UI" \verbatim +// UI 100000.. + opUI_FLUSH = 0x80, // 00 + opUI_READ = 0x81, // 01 + opUI_WRITE = 0x82, // 10 + opUI_BUTTON = 0x83, // 11 + opUI_DRAW = 0x84, // 10000100 + +// \endverbatim \ref cTimer "TIMER" \verbatim + opTIMER_WAIT = 0x85, // 10000101 + opTIMER_READY = 0x86, // 10000110 + opTIMER_READ = 0x87, // 10000111 + +// \endverbatim \ref VM \verbatim +// BREAKPOINT 10001... + opBP0 = 0x88, // 000 + opBP1 = 0x89, // 001 + opBP2 = 0x8A, // 010 + opBP3 = 0x8B, // 011 + opBP_SET = 0x8C, // 10001100 + opMATH = 0x8D, // 10001101 + opRANDOM = 0x8E, // 10001110 + +// \endverbatim \ref cTimer "TIMER" \verbatim + opTIMER_READ_US = 0x8F, // 10001111 + +// \endverbatim \ref cUi "UI" \verbatim + opKEEP_ALIVE = 0x90, // 10010000 + +// \endverbatim \ref cCom "COM" \verbatim +// 100100 + opCOM_READ = 0x91, // 01 + opCOM_WRITE = 0x92, // 10 + +// \endverbatim \ref cSound "SOUND" \verbatim +// 100101 + opSOUND = 0x94, // 00 + opSOUND_TEST = 0x95, // 01 + opSOUND_READY = 0x96, // 10 + +// \endverbatim \ref cInput "INPUT" \verbatim +// + opINPUT_SAMPLE = 0x97, // 10010111 + +// 10011... + opINPUT_DEVICE_LIST = 0x98, // 000 + opINPUT_DEVICE = 0x99, // 001 + opINPUT_READ = 0x9A, // 010 + opINPUT_TEST = 0x9B, // 011 + opINPUT_READY = 0x9C, // 100 + opINPUT_READSI = 0x9D, // 101 + opINPUT_READEXT = 0x9E, // 110 + opINPUT_WRITE = 0x9F, // 111 +// \endverbatim \ref cOutput "OUTPUT" \verbatim +// 101..... + opOUTPUT_GET_TYPE = 0xA0, // 00000 + opOUTPUT_SET_TYPE = 0xA1, // 00001 + opOUTPUT_RESET = 0xA2, // 00010 + opOUTPUT_STOP = 0xA3, // 00011 + opOUTPUT_POWER = 0xA4, // 00100 + opOUTPUT_SPEED = 0xA5, // 00101 + opOUTPUT_START = 0xA6, // 00110 + opOUTPUT_POLARITY = 0xA7, // 00111 + opOUTPUT_READ = 0xA8, // 01000 + opOUTPUT_TEST = 0xA9, // 01001 + opOUTPUT_READY = 0xAA, // 01010 + opOUTPUT_POSITION = 0xAB, // 01011 + opOUTPUT_STEP_POWER = 0xAC, // 01100 + opOUTPUT_TIME_POWER = 0xAD, // 01101 + opOUTPUT_STEP_SPEED = 0xAE, // 01110 + opOUTPUT_TIME_SPEED = 0xAF, // 01111 + + opOUTPUT_STEP_SYNC = 0xB0, // 10000 + opOUTPUT_TIME_SYNC = 0xB1, // 10001 + opOUTPUT_CLR_COUNT = 0xB2, // 10010 + opOUTPUT_GET_COUNT = 0xB3, // 10011 + + opOUTPUT_PRG_STOP = 0xB4, // 10100 + +// \endverbatim \ref cMemory "MEMORY" \verbatim +// 11000... + opFILE = 0xC0, // 000 + opARRAY = 0xC1, // 001 + opARRAY_WRITE = 0xC2, // 010 + opARRAY_READ = 0xC3, // 011 + opARRAY_APPEND = 0xC4, // 100 + opMEMORY_USAGE = 0xC5, // 101 + opFILENAME = 0xC6, // 110 + +// \endverbatim \ref cMove "READ" \verbatim +// 110010.. + opREAD8 = 0xC8, // 00 + opREAD16 = 0xC9, // 01 + opREAD32 = 0xCA, // 10 + opREADF = 0xCB, // 11 + +// \endverbatim \ref cMove "WRITE" \verbatim +// 110011.. + opWRITE8 = 0xCC, // 00 + opWRITE16 = 0xCD, // 01 + opWRITE32 = 0xCE, // 10 + opWRITEF = 0xCF, // 11 + +// \endverbatim \ref cCom "COM" \verbatim +// 11010... + opCOM_READY = 0xD0, // 000 + opCOM_READDATA = 0xD1, // 001 + opCOM_WRITEDATA = 0xD2, // 010 + opCOM_GET = 0xD3, // 011 + opCOM_SET = 0xD4, // 100 + opCOM_TEST = 0xD5, // 101 + opCOM_REMOVE = 0xD6, // 110 + opCOM_WRITEFILE = 0xD7, // 111 + +// 11011... + opMAILBOX_OPEN = 0xD8, // 000 + opMAILBOX_WRITE = 0xD9, // 001 + opMAILBOX_READ = 0xDA, // 010 + opMAILBOX_TEST = 0xDB, // 011 + opMAILBOX_READY = 0xDC, // 100 + opMAILBOX_CLOSE = 0xDD, // 101 + +// SPARE 111..... + +// \endverbatim \ref TST \verbatim + opTST = 0xFF // 11111111 +} +OP; +/* + * \endverbatim + */ + + +//! \page uireadsubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + GET_VBATT = 1, + GET_IBATT = 2, + GET_OS_VERS = 3, + GET_EVENT = 4, + GET_TBATT = 5, + GET_IINT = 6, + GET_IMOTOR = 7, + GET_STRING = 8, + GET_HW_VERS = 9, + GET_FW_VERS = 10, + GET_FW_BUILD = 11, + GET_OS_BUILD = 12, + GET_ADDRESS = 13, + GET_CODE = 14, + KEY = 15, + GET_SHUTDOWN = 16, + GET_WARNING = 17, + GET_LBATT = 18, + TEXTBOX_READ = 21, + GET_VERSION = 26, + GET_IP = 27, + GET_POWER = 29, + GET_SDCARD = 30, + GET_USBSTICK = 31, + + UI_READ_SUBCODES +} +UI_READ_SUBCODE; + +//! \endverbatim + + +//! \page uiwritesubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + WRITE_FLUSH = 1, + FLOATVALUE = 2, + STAMP = 3, + PUT_STRING = 8, + VALUE8 = 9, + VALUE16 = 10, + VALUE32 = 11, + VALUEF = 12, + ADDRESS = 13, + CODE = 14, + DOWNLOAD_END = 15, + SCREEN_BLOCK = 16, + ALLOW_PULSE = 17, + SET_PULSE = 18, + TEXTBOX_APPEND = 21, + SET_BUSY = 22, + SET_TESTPIN = 24, + INIT_RUN = 25, + UPDATE_RUN = 26, + LED = 27, + POWER = 29, + GRAPH_SAMPLE = 30, + TERMINAL = 31, + + UI_WRITE_SUBCODES +} +UI_WRITE_SUBCODE; + +//! \endverbatim + + +//! \page uibuttonsubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + SHORTPRESS = 1, + LONGPRESS = 2, + WAIT_FOR_PRESS = 3, + FLUSH = 4, + PRESS = 5, + RELEASE = 6, + GET_HORZ = 7, + GET_VERT = 8, + PRESSED = 9, + SET_BACK_BLOCK = 10, + GET_BACK_BLOCK = 11, + TESTSHORTPRESS = 12, + TESTLONGPRESS = 13, + GET_BUMBED = 14, + GET_CLICK = 15, + + UI_BUTTON_SUBCODES +} +UI_BUTTON_SUBCODE; + +//! \endverbatim + + +//! \page comreadsubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + COMMAND = 14, + + COM_READ_SUBCODES +} +COM_READ_SUBCODE; + +//! \endverbatim + + +//! \page comwritesubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + REPLY = 14, + + COM_WRITE_SUBCODES +} +COM_WRITE_SUBCODE; + +//! \endverbatim + + +//! \page comgetsubcode Specific command parameter +//! +//! \verbatim +//! + +typedef enum +{ + GET_ON_OFF = 1, //!< Set, Get + GET_VISIBLE = 2, //!< Set, Get + GET_RESULT = 4, //!< Get + GET_PIN = 5, //!< Set, Get + SEARCH_ITEMS = 8, //!< Get + SEARCH_ITEM = 9, //!< Get + FAVOUR_ITEMS = 10, //!< Get + FAVOUR_ITEM = 11, //!< Get + GET_ID = 12, + GET_BRICKNAME = 13, + GET_NETWORK = 14, + GET_PRESENT = 15, + GET_ENCRYPT = 16, + CONNEC_ITEMS = 17, + CONNEC_ITEM = 18, + GET_INCOMING = 19, + GET_MODE2 = 20, + + COM_GET_SUBCODES +} +COM_GET_SUBCODE; + +//! \endverbatim + + +//! \page comsetsubcode Specific command parameter +//! +//! \verbatim +//! + +typedef enum +{ + SET_ON_OFF = 1, //!< Set, Get + SET_VISIBLE = 2, //!< Set, Get + SET_SEARCH = 3, //!< Set + SET_PIN = 5, //!< Set, Get + SET_PASSKEY = 6, //!< Set + SET_CONNECTION = 7, //!< Set + SET_BRICKNAME = 8, + SET_MOVEUP = 9, + SET_MOVEDOWN = 10, + SET_ENCRYPT = 11, + SET_SSID = 12, + SET_MODE2 = 13, + + COM_SET_SUBCODES +} +COM_SET_SUBCODE; + +//! \endverbatim + + +//! \page inputdevicesubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + INSERT_TYPE = 1, + GET_FORMAT = 2, + CAL_MINMAX = 3, + CAL_DEFAULT = 4, + GET_TYPEMODE = 5, + GET_SYMBOL = 6, + CAL_MIN = 7, + CAL_MAX = 8, + SETUP = 9, + CLR_ALL = 10, + GET_RAW = 11, + GET_CONNECTION = 12, + STOP_ALL = 13, + SET_TYPEMODE = 14, + READY_IIC = 15, + GET_NAME = 21, + GET_MODENAME = 22, + SET_RAW = 23, + GET_FIGURES = 24, + GET_CHANGES = 25, + CLR_CHANGES = 26, + READY_PCT = 27, + READY_RAW = 28, + READY_SI = 29, + GET_MINMAX = 30, + GET_BUMPS = 31, + + INPUT_DEVICESUBCODES +} +INPUT_DEVICE_SUBCODE; + +//! \endverbatim + + +//! \page programinfosubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + OBJ_STOP = 0, // VM + OBJ_START = 4, // VM + GET_STATUS = 22, // VM + GET_SPEED = 23, // VM + GET_PRGRESULT = 24, // VM + SET_INSTR = 25, // VM + + PROGRAM_INFO_SUBCODES, +} +PROGRAM_INFO_SUBCODE; + +//! \endverbatim + + +//! \page uidrawsubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + UPDATE = 0, + CLEAN = 1, + PIXEL = 2, + LINE = 3, + CIRCLE = 4, + TEXT = 5, + ICON = 6, + PICTURE = 7, + VALUE = 8, + FILLRECT = 9, + RECT = 10, + NOTIFICATION = 11, + QUESTION = 12, + KEYBOARD = 13, + BROWSE = 14, + VERTBAR = 15, + INVERSERECT = 16, + SELECT_FONT = 17, + TOPLINE = 18, + FILLWINDOW = 19, + SCROLL = 20, + DOTLINE = 21, + VIEW_VALUE = 22, + VIEW_UNIT = 23, + FILLCIRCLE = 24, + STORE = 25, + RESTORE = 26, + ICON_QUESTION = 27, + BMPFILE = 28, + POPUP = 29, + GRAPH_SETUP = 30, + GRAPH_DRAW = 31, + TEXTBOX = 32, + + UI_DRAW_SUBCODES +} +UI_DRAW_SUBCODE; + +//! \endverbatim + + +//! \page memoryfilesubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + OPEN_APPEND = 0, + OPEN_READ = 1, + OPEN_WRITE = 2, + READ_VALUE = 3, + WRITE_VALUE = 4, + READ_TEXT = 5, + WRITE_TEXT = 6, + CLOSE = 7, + LOAD_IMAGE = 8, + GET_HANDLE = 9, + MAKE_FOLDER = 10, + GET_POOL = 11, + SET_LOG_SYNC_TIME = 12, + GET_FOLDERS = 13, + GET_LOG_SYNC_TIME = 14, + GET_SUBFOLDER_NAME = 15, + WRITE_LOG = 16, + CLOSE_LOG = 17, + GET_IMAGE = 18, + GET_ITEM = 19, + GET_CACHE_FILES = 20, + PUT_CACHE_FILE = 21, + GET_CACHE_FILE = 22, + DEL_CACHE_FILE = 23, + DEL_SUBFOLDER = 24, + GET_LOG_NAME = 25, + + OPEN_LOG = 27, + READ_BYTES = 28, + WRITE_BYTES = 29, + REMOVE = 30, + MOVE = 31, + + FILE_SUBCODES +} +FILE_SUBCODE; + +//! \endverbatim + + +//! \page memoryarraysubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + DELETE = 0, + CREATE8 = 1, + CREATE16 = 2, + CREATE32 = 3, + CREATEF = 4, + RESIZE = 5, + FILL = 6, + COPY = 7, + INIT8 = 8, + INIT16 = 9, + INIT32 = 10, + INITF = 11, + SIZE = 12, + READ_CONTENT = 13, + WRITE_CONTENT = 14, + READ_SIZE = 15, + + ARRAY_SUBCODES +} +ARRAY_SUBCODE; + +//! \endverbatim + + +//! \page memoryfilenamesubcode Specific command parameter +//! +//! \verbatim +//! + +typedef enum +{ + EXIST = 16, //!< MUST BE GREATER OR EQUAL TO "ARRAY_SUBCODES" + TOTALSIZE = 17, + SPLIT = 18, + MERGE = 19, + CHECK = 20, + PACK = 21, + UNPACK = 22, + GET_FOLDERNAME = 23, + + FILENAME_SUBCODES +} +FILENAME_SUBCODE; + +//! \endverbatim + + +//! \page infosubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + SET_ERROR = 1, + GET_ERROR = 2, + ERRORTEXT = 3, + + GET_VOLUME = 4, + SET_VOLUME = 5, + GET_MINUTES = 6, + SET_MINUTES = 7, + + INFO_SUBCODES +} +INFO_SUBCODE; + +//! \endverbatim + + +//! \page soundsubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + BREAK = 0, + TONE = 1, + PLAY = 2, + REPEAT = 3, + SERVICE = 4, + + SOUND_SUBCODES +} +SOUND_SUBCODE; + +//! \endverbatim + + +//! \page stringsubcode Specific command parameter +//! +//! +//! \verbatim +//! + +typedef enum +{ + GET_SIZE = 1, // VM get string size + ADD = 2, // VM add two strings + COMPARE = 3, // VM compare two strings + DUPLICATE = 5, // VM duplicate one string to another + VALUE_TO_STRING = 6, + STRING_TO_VALUE = 7, + STRIP = 8, + NUMBER_TO_STRING = 9, + SUB = 10, + VALUE_FORMATTED = 11, + NUMBER_FORMATTED = 12, + + STRING_SUBCODES +} +STRING_SUBCODE; + +//! \endverbatim + + +/*! \page types + +
+ +TYPE VALUES FOR BYTE CODES: +\n + \verbatim */ + +typedef enum +{ + MODE_KEEP = -1, //!< Mode value that won't change mode in byte codes (convenient place to define) + TYPE_KEEP = 0, //!< Type value that won't change type in byte codes + + // Types defined in "typedata.rcf" + TYPE_NXT_TOUCH = 1, //!< Device is NXT touch sensor + TYPE_NXT_LIGHT = 2, //!< Device is NXT light sensor + TYPE_NXT_SOUND = 3, //!< Device is NXT sound sensor + TYPE_NXT_COLOR = 4, //!< Device is NXT color sensor + TYPE_NXT_ULTRASONIC = 5, //!< Device is NXT ultra sonic sensor + TYPE_NXT_TEMPERATURE = 6, //!< Device is NXT temperature sensor + TYPE_TACHO = 7, //!< Device is EV3/NXT tacho motor + TYPE_MINITACHO = 8, //!< Device is EV3 mini tacho motor + TYPE_NEWTACHO = 9, //!< Device is EV3 new tacho motor + + TYPE_TOUCH = 16, //!< Device is EV3 touch sensor + + // Types defined in known EV3 digital devices + TYPE_COLOR = 29, //!< Device is EV3 color sensor + TYPE_ULTRASONIC = 30, //!< Device is EV3 ultra sonic sensor + TYPE_GYRO = 32, //!< Device is EV3 gyro sensor + TYPE_IR = 33, //!< Device is EV3 IR sensor + + // Type range reserved for third party devices + TYPE_THIRD_PARTY_START = 50, + TYPE_THIRD_PARTY_END = 98, + + // Special types + TYPE_ENERGYMETER = 99, //!< Device is energy meter + TYPE_IIC_UNKNOWN = 100, //!< Device type is not known yet + TYPE_NXT_TEST = 101, //!< Device is a NXT ADC test sensor + + TYPE_NXT_IIC = 123, //!< Device is NXT IIC sensor + TYPE_TERMINAL = 124, //!< Port is connected to a terminal + TYPE_UNKNOWN = 125, //!< Port not empty but type has not been determined + TYPE_NONE = 126, //!< Port empty or not available + TYPE_ERROR = 127, //!< Port not empty and type is invalid +} +TYPE; + + +/* \endverbatim */ + + +/*! \page programid Program ID's (Slots) + + \anchor prgid + + \verbatim */ + +typedef enum +{ + GUI_SLOT = 0, //!< Program slot reserved for executing the user interface + USER_SLOT = 1, //!< Program slot used to execute user projects, apps and tools + CMD_SLOT = 2, //!< Program slot used for direct commands coming from c_com + TERM_SLOT = 3, //!< Program slot used for direct commands coming from c_ui + DEBUG_SLOT = 4, //!< Program slot used to run the debug ui + + SLOTS, //!< Maximum slots supported by the VM + + // ONLY VALID IN opPROGRAM_STOP + CURRENT_SLOT = -1 +} +SLOT; + +/* \endverbatim */ + + +/*! \page buttons Button + + \verbatim */ + +typedef enum +{ + NO_BUTTON = 0, + UP_BUTTON = 1, + ENTER_BUTTON = 2, + DOWN_BUTTON = 3, + RIGHT_BUTTON = 4, + LEFT_BUTTON = 5, + BACK_BUTTON = 6, + ANY_BUTTON = 7, + + BUTTONTYPES = 8 +} +BUTTONTYPE; + +/* \endverbatim */ + + +/*! \page mathsubcode Specific command parameter + + \verbatim */ + +typedef enum +{ + EXP = 1, //!< e^x r = expf(x) + MOD = 2, //!< Modulo r = fmod(x,y) + FLOOR = 3, //!< Floor r = floor(x) + CEIL = 4, //!< Ceiling r = ceil(x) + ROUND = 5, //!< Round r = round(x) + ABS = 6, //!< Absolute r = fabs(x) + NEGATE = 7, //!< Negate r = 0.0 - x + SQRT = 8, //!< Squareroot r = sqrt(x) + LOG = 9, //!< Log r = log10(x) + LN = 10, //!< Ln r = log(x) + SIN = 11, //!< + COS = 12, //!< + TAN = 13, //!< + ASIN = 14, //!< + ACOS = 15, //!< + ATAN = 16, //!< + MOD8 = 17, //!< Modulo DATA8 r = x % y + MOD16 = 18, //!< Modulo DATA16 r = x % y + MOD32 = 19, //!< Modulo DATA32 r = x % y + POW = 20, //!< Exponent r = powf(x,y) + TRUNC = 21, //!< Truncate r = (float)((int)(x * pow(y))) / pow(y) + + MATHTYPES //!< Maximum number of math functions supported by the VM +} +MATHTYPE; + +/* \endverbatim */ + + +//! \page tstsubcode Specific command parameter +//! +//! \verbatim +//! + +typedef enum +{ + TST_OPEN = 10, //!< MUST BE GREATER OR EQUAL TO "INFO_SUBCODES" + TST_CLOSE = 11, + TST_READ_PINS = 12, + TST_WRITE_PINS = 13, + TST_READ_ADC = 14, + TST_WRITE_UART = 15, + TST_READ_UART = 16, + TST_ENABLE_UART = 17, + TST_DISABLE_UART = 18, + TST_ACCU_SWITCH = 19, + TST_BOOT_MODE2 = 20, + TST_POLL_MODE2 = 21, + TST_CLOSE_MODE2 = 22, + TST_RAM_CHECK = 23, + + TST_SUBCODES +} +TST_SUBCODE; + +//! \endverbatim + + +/*! \page browsers Browser Types Avaliable + + \verbatim */ + +typedef enum +{ + BROWSE_FOLDERS = 0, //!< Browser for folders + BROWSE_FOLDS_FILES = 1, //!< Browser for folders and files + BROWSE_CACHE = 2, //!< Browser for cached / recent files + BROWSE_FILES = 3, //!< Browser for files + + BROWSERTYPES //!< Maximum font types supported by the VM +} +BROWSERTYPE; + +/* \endverbatim */ + + +/*! \page fonts Font Types Avaliable + + \verbatim */ + +typedef enum +{ + NORMAL_FONT = 0, + SMALL_FONT = 1, + LARGE_FONT = 2, + TINY_FONT = 3, + + FONTTYPES //!< Maximum font types supported by the VM +} +FONTTYPE; + +/* \endverbatim */ + + +/*! \page icons Icon Types Avaliable + + \verbatim */ + +typedef enum +{ + NORMAL_ICON = 0, //!< "24x12_Files_Folders_Settings.bmp" + SMALL_ICON = 1, + LARGE_ICON = 2, //!< "24x22_Yes_No_OFF_FILEOps.bmp" + MENU_ICON = 3, + ARROW_ICON = 4, //!< "8x12_miniArrows.bmp" + + ICONTYPES //!< Maximum icon types supported by the VM +} +ICONTYPE; + + +typedef enum +{ + SICON_CHARGING = 0, + SICON_BATT_4 = 1, + SICON_BATT_3 = 2, + SICON_BATT_2 = 3, + SICON_BATT_1 = 4, + SICON_BATT_0 = 5, + SICON_WAIT1 = 6, + SICON_WAIT2 = 7, + SICON_BT_ON = 8, + SICON_BT_VISIBLE = 9, + SICON_BT_CONNECTED = 10, + SICON_BT_CONNVISIB = 11, + SICON_WIFI_3 = 12, + SICON_WIFI_2 = 13, + SICON_WIFI_1 = 14, + SICON_WIFI_CONNECTED = 15, + + SICON_USB = 21, + + S_ICON_NOS +} +S_ICON_NO; + + +typedef enum +{ + ICON_NONE = -1, + ICON_RUN = 0, + ICON_FOLDER = 1, + ICON_FOLDER2 = 2, + ICON_USB = 3, + ICON_SD = 4, + ICON_SOUND = 5, + ICON_IMAGE = 6, + ICON_SETTINGS = 7, + ICON_ONOFF = 8, + ICON_SEARCH = 9, + ICON_WIFI = 10, + ICON_CONNECTIONS = 11, + ICON_ADD_HIDDEN = 12, + ICON_TRASHBIN = 13, + ICON_VISIBILITY = 14, + ICON_KEY = 15, + ICON_CONNECT = 16, + ICON_DISCONNECT = 17, + ICON_UP = 18, + ICON_DOWN = 19, + ICON_WAIT1 = 20, + ICON_WAIT2 = 21, + ICON_BLUETOOTH = 22, + ICON_INFO = 23, + ICON_TEXT = 24, + + + ICON_QUESTIONMARK = 27, + ICON_INFO_FILE = 28, + ICON_DISC = 29, + ICON_CONNECTED = 30, + ICON_OBP = 31, + ICON_OBD = 32, + ICON_OPENFOLDER = 33, + ICON_BRICK1 = 34, + N_ICON_NOS +} +N_ICON_NO; + + +typedef enum +{ + YES_NOTSEL = 0, + YES_SEL = 1, + NO_NOTSEL = 2, + NO_SEL = 3, + OFF = 4, + WAIT_VERT = 5, + WAIT_HORZ = 6, + TO_MANUAL = 7, + WARNSIGN = 8, + WARN_BATT = 9, + WARN_POWER = 10, + WARN_TEMP = 11, + NO_USBSTICK = 12, + TO_EXECUTE = 13, + TO_BRICK = 14, + TO_SDCARD = 15, + TO_USBSTICK = 16, + TO_BLUETOOTH = 17, + TO_WIFI = 18, + TO_TRASH = 19, + TO_COPY = 20, + TO_FILE = 21, + CHAR_ERROR = 22, + COPY_ERROR = 23, + PROGRAM_ERROR = 24, + + + WARN_MEMORY = 27, + L_ICON_NOS +} +L_ICON_NO; + + +typedef enum +{ + ICON_STAR = 0, + ICON_LOCKSTAR = 1, + ICON_LOCK = 2, + ICON_PC = 3, //!< Bluetooth type PC + ICON_PHONE = 4, //!< Bluetooth type PHONE + ICON_BRICK = 5, //!< Bluetooth type BRICK + ICON_UNKNOWN = 6, //!< Bluetooth type UNKNOWN + ICON_FROM_FOLDER = 7, + ICON_CHECKBOX = 8, + ICON_CHECKED = 9, + ICON_XED = 10, + + M_ICON_NOS +} +M_ICON_NO; + + +typedef enum +{ + + ICON_LEFT = 1, + ICON_RIGHT = 2, + + A_ICON_NOS +} +A_ICON_NO; + + +/* \endverbatim */ + + +/*! \page bttypes Bluetooth Device Types + + \verbatim */ + +typedef enum +{ + BTTYPE_PC = 3, //!< Bluetooth type PC + BTTYPE_PHONE = 4, //!< Bluetooth type PHONE + BTTYPE_BRICK = 5, //!< Bluetooth type BRICK + BTTYPE_UNKNOWN = 6, //!< Bluetooth type UNKNOWN + + BTTYPES +} +BTTYPE; + + +/* \endverbatim */ + + +/*! \page ledpatterns LED Pattern + + \verbatim */ + +typedef enum +{ + LED_BLACK = 0, + LED_GREEN = 1, + LED_RED = 2, + LED_ORANGE = 3, + LED_GREEN_FLASH = 4, + LED_RED_FLASH = 5, + LED_ORANGE_FLASH = 6, + LED_GREEN_PULSE = 7, + LED_RED_PULSE = 8, + LED_ORANGE_PULSE = 9, + + LEDPATTERNS +} +LEDPATTERN; + + +/* \endverbatim */ + + +typedef enum +{ + LED_ALL = 0, //!< All LEDs + LED_RR = 1, //!< Right red + LED_RG = 2, //!< Right green + LED_LR = 3, //!< Left red + LED_LG = 4 //!< Left green +} +LEDTYPE; + + +/*! \page filetypes File Types Avaliable + + \verbatim */ + + +typedef enum +{ + FILETYPE_UNKNOWN = 0x00, + TYPE_FOLDER = 0x01, + TYPE_SOUND = 0x02, + TYPE_BYTECODE = 0x03, + TYPE_GRAPHICS = 0x04, + TYPE_DATALOG = 0x05, + TYPE_PROGRAM = 0x06, + TYPE_TEXT = 0x07, + TYPE_SDCARD = 0x10, + TYPE_USBSTICK = 0x20, + + FILETYPES, //!< Maximum icon types supported by the VM + + TYPE_RESTART_BROWSER = -1, + TYPE_REFRESH_BROWSER = -2 +} +FILETYPE; + +/* \endverbatim */ + + +/*! \page results Results + + Describes result from executing functions + + \verbatim */ + + +typedef enum +{ + OK = 0, //!< No errors to report + BUSY = 1, //!< Busy - try again + FAIL = 2, //!< Something failed + STOP = 4, //!< Stopped + START = 8 //!< Start +} +RESULT; + +/* \endverbatim */ + + +/*! \page formats + * + * Data formats used in device type formats + * \verbatim + */ + +typedef enum +{ + DATA_8 = 0x00, //!< DATA8 (don't change) + DATA_16 = 0x01, //!< DATA16 (don't change) + DATA_32 = 0x02, //!< DATA32 (don't change) + DATA_F = 0x03, //!< DATAF (don't change) + DATA_S = 0x04, //!< Zero terminated string + DATA_A = 0x05, //!< Array handle + + DATA_V = 0x07, //!< Variable type + + DATA_PCT = 0x10, //!< Percent (used in opINPUT_READEXT) + DATA_RAW = 0x12, //!< Raw (used in opINPUT_READEXT) + DATA_SI = 0x13, //!< SI unit (used in opINPUT_READEXT) + + DATA_FORMATS +} +DATA_FORMAT; + +/*\endverbatim + * + * \n + */ + + +/*! \page delimiters + * + * Delimiter codes used to define how data is separated in files + * \verbatim + */ + +typedef enum +{ + DEL_NONE = 0, //!< No delimiter at all + DEL_TAB = 1, //!< Use tab as delimiter + DEL_SPACE = 2, //!< Use space as delimiter + DEL_RETURN = 3, //!< Use return as delimiter + DEL_COLON = 4, //!< Use colon as delimiter + DEL_COMMA = 5, //!< Use comma as delimiter + DEL_LINEFEED = 6, //!< Use line feed as delimiter + DEL_CRLF = 7, //!< Use return+line feed as delimiter + + DELS +} +DEL; + +/*\endverbatim + * + * \n + */ + + +/*! \page transportlayers Hardware Transport Layer + + \verbatim */ + +typedef enum +{ + HW_USB = 1, + HW_BT = 2, + HW_WIFI = 3, + + HWTYPES +} +HWTYPE; + +/* \endverbatim */ + + +/*! \page encryptions Encryption Types + + \verbatim */ + +typedef enum +{ + ENCRYPT_NONE = 0, + ENCRYPT_WPA2 = 1, + + ENCRYPTS +} +ENCRYPT; + +/* \endverbatim */ + + +typedef enum +{ + RED = 0, + GREEN = 1, + BLUE = 2, + BLANK = 3, + COLORS +} +COLOR; + + +/* Constants related to color sensor value using */ +/* Color sensor as color detector */ +typedef enum +{ + BLACKCOLOR = 1, + BLUECOLOR = 2, + GREENCOLOR = 3, + YELLOWCOLOR = 4, + REDCOLOR = 5, + WHITECOLOR = 6 +} +NXTCOLOR; + + +/*! \page warnings WARNINGS + + \verbatim */ + +typedef enum +{ + WARNING_TEMP = 0x01, + WARNING_CURRENT = 0x02, + WARNING_VOLTAGE = 0x04, + WARNING_MEMORY = 0x08, + WARNING_DSPSTAT = 0x10, + WARNING_RAM = 0x20, + WARNING_BATTLOW = 0x40, + WARNING_BUSY = 0x80, + + WARNINGS = 0x3F +} +WARNING; + +/* \endverbatim */ + + +#define DATA8_NAN ((DATA8)(-128)) +#define DATA16_NAN ((DATA16)(-32768)) +#define DATA32_NAN ((DATA32)(0x80000000)) +#define DATAF_NAN ((float)0 / (float)0) //(0x7FC00000) + +#define DATA8_MIN vmDATA8_MIN +#define DATA8_MAX vmDATA8_MAX +#define DATA16_MIN vmDATA16_MIN +#define DATA16_MAX vmDATA16_MAX +#define DATA32_MIN vmDATA32_MIN +#define DATA32_MAX vmDATA32_MAX +#define DATAF_MIN vmDATAF_MIN +#define DATAF_MAX vmDATAF_MAX + + + + +/*! \enum OBJSTAT + * + * Values used to describe an object's status + */ +typedef enum +{ + RUNNING = 0x0010, //!< Object code is running + WAITING = 0x0020, //!< Object is waiting for final trigger + STOPPED = 0x0040, //!< Object is stopped or not triggered yet + HALTED = 0x0080, //!< Object is halted because a call is in progress +} +OBJSTAT; + + +/*! \page devicecommands + * + * Device commands used to control (UART sensors) devices + * \verbatim + */ + +typedef enum +{ + DEVCMD_RESET = 0x11, //!< UART device reset + DEVCMD_FIRE = 0x11, //!< UART device fire (ultrasonic) + DEVCMD_CHANNEL = 0x12, //!< UART device channel (IR seeker) + + DEVCMDS +} +DEVCMD; + +/*\endverbatim + * + * \n + */ + + +// GRAPHICS + +#define vmPOP3_ABS_X 16 // +#define vmPOP3_ABS_Y 50 // + +#define vmPOP3_ABS_WARN_ICON_X 64 +#define vmPOP3_ABS_WARN_ICON_X1 40 +#define vmPOP3_ABS_WARN_ICON_X2 72 +#define vmPOP3_ABS_WARN_ICON_X3 104 +#define vmPOP3_ABS_WARN_ICON_Y 60 +#define vmPOP3_ABS_WARN_SPEC_ICON_X 88 +#define vmPOP3_ABS_WARN_SPEC_ICON_Y 60 +#define vmPOP3_ABS_WARN_TEXT_X 80 +#define vmPOP3_ABS_WARN_TEXT_Y 68 +#define vmPOP3_ABS_WARN_YES_X 72 +#define vmPOP3_ABS_WARN_YES_Y 90 +#define vmPOP3_ABS_WARN_LINE_X 21 +#define vmPOP3_ABS_WARN_LINE_Y 89 +#define vmPOP3_ABS_WARN_LINE_ENDX 155 + + +#define LONGToBytes(_x) (UBYTE)((_x) & 0xFF),(UBYTE)((_x >> 8) & 0xFF),(UBYTE)((_x >> 16) & 0xFF),(UBYTE)((_x >> 24) & 0xFF) +#define WORDToBytes(_x) (UBYTE)((_x) & 0xFF),(UBYTE)((_x >> 8) & 0xFF) +#define BYTEToBytes(_x) (UBYTE)((_x) & 0xFF) + +#define PROGRAMHeader(VersionInfo,NumberOfObjects,GlobalBytes)\ + 'L','E','G','O',LONGToBytes(0),WORDToBytes((UWORD)(BYTECODE_VERSION * 100.0)),WORDToBytes(NumberOfObjects),LONGToBytes(GlobalBytes) + +#define VMTHREADHeader(OffsetToInstructions,LocalBytes)\ + LONGToBytes(OffsetToInstructions),0,0,0,0,LONGToBytes(LocalBytes) + +#define SUBCALLHeader(OffsetToInstructions,LocalBytes)\ + LONGToBytes(OffsetToInstructions),0,0,1,0,LONGToBytes(LocalBytes) + +#define BLOCKHeader(OffsetToInstructions,OwnerObjectId,TriggerCount)\ + LONGToBytes(OffsetToInstructions),WORDToBytes(OwnerObjectId),WORDToBytes(TriggerCount),LONGToBytes(0) + +// MACROS FOR PRIMITIVES AND SYSTEM CALLS + +#define PRIMPAR_SHORT 0x00 +#define PRIMPAR_LONG 0x80 + +#define PRIMPAR_CONST 0x00 +#define PRIMPAR_VARIABEL 0x40 +#define PRIMPAR_LOCAL 0x00 +#define PRIMPAR_GLOBAL 0x20 +#define PRIMPAR_HANDLE 0x10 +#define PRIMPAR_ADDR 0x08 + +#define PRIMPAR_INDEX 0x1F +#define PRIMPAR_CONST_SIGN 0x20 +#define PRIMPAR_VALUE 0x3F + +#define PRIMPAR_BYTES 0x07 + +#define PRIMPAR_STRING_OLD 0 +#define PRIMPAR_1_BYTE 1 +#define PRIMPAR_2_BYTES 2 +#define PRIMPAR_4_BYTES 3 +#define PRIMPAR_STRING 4 + +#define PRIMPAR_LABEL 0x20 + +#define HND(x) PRIMPAR_HANDLE | x +#define ADR(x) PRIMPAR_ADDR | x + +#define LCS (PRIMPAR_LONG | PRIMPAR_STRING) + +#define LAB1(v) (PRIMPAR_LONG | PRIMPAR_LABEL),(v & 0xFF) + +#define LC0(v) ((v & PRIMPAR_VALUE) | PRIMPAR_SHORT | PRIMPAR_CONST) +#define LC1(v) (PRIMPAR_LONG | PRIMPAR_CONST | PRIMPAR_1_BYTE),(v & 0xFF) +#define LC2(v) (PRIMPAR_LONG | PRIMPAR_CONST | PRIMPAR_2_BYTES),(v & 0xFF),((v >> 8) & 0xFF) +#define LC4(v) (PRIMPAR_LONG | PRIMPAR_CONST | PRIMPAR_4_BYTES),((ULONG)v & 0xFF),(((ULONG)v >> (ULONG)8) & 0xFF),(((ULONG)v >> (ULONG)16) & 0xFF),(((ULONG)v >> (ULONG)24) & 0xFF) + +#define LV0(i) ((i & PRIMPAR_INDEX) | PRIMPAR_SHORT | PRIMPAR_VARIABEL | PRIMPAR_LOCAL) +#define LV1(i) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_LOCAL | PRIMPAR_1_BYTE),(i & 0xFF) +#define LV2(i) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_LOCAL | PRIMPAR_2_BYTES),(i & 0xFF),((i >> 8) & 0xFF) +#define LV4(i) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_LOCAL | PRIMPAR_4_BYTES),(i & 0xFF),((i >> 8) & 0xFF),((i >> 16) & 0xFF),((i >> 24) & 0xFF) + +#define GV0(i) ((i & PRIMPAR_INDEX) | PRIMPAR_SHORT | PRIMPAR_VARIABEL | PRIMPAR_GLOBAL) +#define GV1(i) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_GLOBAL | PRIMPAR_1_BYTE),(i & 0xFF) +#define GV2(i) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_GLOBAL | PRIMPAR_2_BYTES),(i & 0xFF),((i >> 8) & 0xFF) +#define GV4(i) (PRIMPAR_LONG | PRIMPAR_VARIABEL | PRIMPAR_GLOBAL | PRIMPAR_4_BYTES),(i & 0xFF),((i >> 8) & 0xFF),((i >> 16) & 0xFF),((i >> 24) & 0xFF) + +// MACROS FOR SUB CALLS + + +#define CALLPAR_IN 0x80 +#define CALLPAR_OUT 0x40 + +#define CALLPAR_TYPE 0x07 +#define CALLPAR_DATA8 DATA_8 +#define CALLPAR_DATA16 DATA_16 +#define CALLPAR_DATA32 DATA_32 +#define CALLPAR_DATAF DATA_F +#define CALLPAR_STRING DATA_S + +#define IN_8 (CALLPAR_IN | CALLPAR_DATA8) +#define IN_16 (CALLPAR_IN | CALLPAR_DATA16) +#define IN_32 (CALLPAR_IN | CALLPAR_DATA32) +#define IN_F (CALLPAR_IN | CALLPAR_DATAF) +#define IN_S (CALLPAR_IN | CALLPAR_STRING) +#define OUT_8 (CALLPAR_OUT | CALLPAR_DATA8) +#define OUT_16 (CALLPAR_OUT | CALLPAR_DATA16) +#define OUT_32 (CALLPAR_OUT | CALLPAR_DATA32) +#define OUT_F (CALLPAR_OUT | CALLPAR_DATAF) +#define OUT_S (CALLPAR_OUT | CALLPAR_STRING) + +#define IO_8 IN_8 | OUT_8 +#define IO_16 IN_16 | OUT_16 +#define IO_32 IN_32 | OUT_32 +#define IO_F IN_F | OUT_F +#define IO_S IN_S | OUT_S + +#define IN_OUT_8 IO_8 +#define IN_OUT_16 IO_16 +#define IN_OUT_32 IO_32 +#define IN_OUT_F IO_F +#define IN_OUT_S IO_S + +#endif /* BYTECODES_H_ */ diff --git a/brick/kernel/source/c_branch.h b/brick/kernel/source/c_branch.h new file mode 100644 index 00000000..8543c516 --- /dev/null +++ b/brick/kernel/source/c_branch.h @@ -0,0 +1,81 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef C_BRANCH_H_ +#define C_BRANCH_H_ + +void cBranchJr(void); + +void cBranchJrFalse(void); + +void cBranchJrTrue(void); + +void cBranchJrNan(void); + +void cBranchJrLt8(void); + +void cBranchJrLt16(void); + +void cBranchJrLt32(void); + +void cBranchJrLtF(void); + +void cBranchJrGt8(void); + +void cBranchJrGt16(void); + +void cBranchJrGt32(void); + +void cBranchJrGtF(void); + +void cBranchJrLtEq8(void); + +void cBranchJrLtEq16(void); + +void cBranchJrLtEq32(void); + +void cBranchJrLtEqF(void); + +void cBranchJrGtEq8(void); + +void cBranchJrGtEq16(void); + +void cBranchJrGtEq32(void); + +void cBranchJrGtEqF(void); + +void cBranchJrEq8(void); + +void cBranchJrEq16(void); + +void cBranchJrEq32(void); + +void cBranchJrEqF(void); + +void cBranchJrNEq8(void); + +void cBranchJrNEq16(void); + +void cBranchJrNEq32(void); + +void cBranchJrNEqF(void); + +#endif /* C_BRANCH_H_ */ diff --git a/brick/kernel/source/c_compare.h b/brick/kernel/source/c_compare.h new file mode 100644 index 00000000..2f8740ab --- /dev/null +++ b/brick/kernel/source/c_compare.h @@ -0,0 +1,81 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef C_COMPARE_H_ +#define C_COMPARE_H_ + +void cCompareLt8(void); + +void cCompareLt16(void); + +void cCompareLt32(void); + +void cCompareLtF(void); + +void cCompareGt8(void); + +void cCompareGt16(void); + +void cCompareGt32(void); + +void cCompareGtF(void); + +void cCompareEq8(void); + +void cCompareEq16(void); + +void cCompareEq32(void); + +void cCompareEqF(void); + +void cCompareNEq8(void); + +void cCompareNEq16(void); + +void cCompareNEq32(void); + +void cCompareNEqF(void); + +void cCompareLtEq8(void); + +void cCompareLtEq16(void); + +void cCompareLtEq32(void); + +void cCompareLtEqF(void); + +void cCompareGtEq8(void); + +void cCompareGtEq16(void); + +void cCompareGtEq32(void); + +void cCompareGtEqF(void); + +void cCompareSelect8(void); + +void cCompareSelect16(void); + +void cCompareSelect32(void); + +void cCompareSelectF(void); + +#endif /* C_COMPARE_H_ */ diff --git a/brick/kernel/source/c_math.h b/brick/kernel/source/c_math.h new file mode 100644 index 00000000..d8aba73f --- /dev/null +++ b/brick/kernel/source/c_math.h @@ -0,0 +1,88 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef C_MATH_H_ +#define C_MATH_H_ + +#define DegToRad(D) (D * 0.0174532925) + +#define RadToDeg(R) (R * 57.2957795) + + +void cMathAdd8(void); + +void cMathAdd16(void); + +void cMathAdd32(void); + +void cMathAddF(void); + +void cMathSub8(void); + +void cMathSub16(void); + +void cMathSub32(void); + +void cMathSubF(void); + +void cMathMul8(void); + +void cMathMul16(void); + +void cMathMul32(void); + +void cMathMulF(void); + +void cMathDiv8(void); + +void cMathDiv16(void); + +void cMathDiv32(void); + +void cMathDivF(void); + +void cMathOr8(void); + +void cMathOr16(void); + +void cMathOr32(void); + +void cMathAnd8(void); + +void cMathAnd16(void); + +void cMathAnd32(void); + +void cMathXor8(void); + +void cMathXor16(void); + +void cMathXor32(void); + +void cMathRl8(void); + +void cMathRl16(void); + +void cMathRl32(void); + +void cMath(void); + +#endif /* C_MATH_H_ */ diff --git a/brick/kernel/source/c_move.h b/brick/kernel/source/c_move.h new file mode 100644 index 00000000..01233695 --- /dev/null +++ b/brick/kernel/source/c_move.h @@ -0,0 +1,76 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef C_MOVE_H_ +#define C_MOVE_H_ + +void cMove8to8(void); + +void cMove8to16(void); + +void cMove8to32(void); + +void cMove8toF(void); + +void cMove16to8(void); + +void cMove16to16(void); + +void cMove16to32(void); + +void cMove16toF(void); + +void cMove32to8(void); + +void cMove32to16(void); + +void cMove32to32(void); + +void cMove32toF(void); + +void cMoveFto8(void); + +void cMoveFto16(void); + +void cMoveFto32(void); + +void cMoveFtoF(void); + +void cMoveInitBytes(void); + +void cMoveRead8(void); + +void cMoveRead16(void); + +void cMoveRead32(void); + +void cMoveReadF(void); + +void cMoveWrite8(void); + +void cMoveWrite16(void); + +void cMoveWrite32(void); + +void cMoveWriteF(void); + + +#endif /* C_MOVE_H_ */ diff --git a/brick/kernel/source/c_timer.h b/brick/kernel/source/c_timer.h new file mode 100644 index 00000000..809e0877 --- /dev/null +++ b/brick/kernel/source/c_timer.h @@ -0,0 +1,38 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef C_TIMER_H_ +#define C_TIMER_H_ + +#if (HARDWARE != SIMULATION) +ULONG cTimerGetuS(void); +ULONG cTimerGetmS(void); +#endif + +void cTimerWait(void); + +void cTimerReady(void); + +void cTimerRead(void); + +void cTimerReaduS(void); + +#endif /* C_TIMER_H_ */ diff --git a/brick/kernel/source/lms2012.h b/brick/kernel/source/lms2012.h new file mode 100755 index 00000000..72f0d691 --- /dev/null +++ b/brick/kernel/source/lms2012.h @@ -0,0 +1,1566 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception, if other files instantiate templates or use macros or + * inline functions from this file, or you compile this file and link it with + * other works to produce a work based on this file, this file does not by itself + * cause the resulting work to be covered by the GNU General Public License. + * However the source code for this file must still be made available in accordance + * with section (3) of the GNU General Public License. + * + */ + + +/*! \page debug Terminal Configuration + * + \verbatim + + +TERMINAL CONFIGURATION (in top of "lms2012.h") + +Normal release: DEBUG not defined, TERMINAL_ENABLED = 0, DEBUG_UART = 4 + +Printf working: DEBUG not defined, TERMINAL_ENABLED = 0, DEBUG_UART = 0 + +Development with debug messages: DEBUG defined, TERMINAL_ENABLED = 1, DEBUG_UART = 0 + +*/ + + +//#define DEBUG //!< When defined debug messages is output on standard I/O (d_uart is different) + +#define TERMINAL_ENABLED 0 //!< DEBUG terminal enabled (0 = disabled, 1 = enabled) +#define DEBUG_UART 4 //!< UART used for debug (0 = port1, 1 = port2, ... 4 = none) + + +/* \endverbatim */ + +//#define DEBUG_VM +//#define DEBUG_TRACE_TASK +//#define DEBUG_C_COM +//#define DEBUG_C_INPUT +//#define DEBUG_C_INPUT_DAISYCHAIN +//#define DEBUG_C_INPUT_DATALOG +//#define DEBUG_C_INPUT_FAST_DATALOG +//#define DEBUG_C_MEMORY +//#define DEBUG_C_MEMORY_LOG +//#define DEBUG_C_MEMORY_FILE +//#define DEBUG_C_MEMORY_LOW +//#define DEBUG_C_SOUND +//#define DEBUG_C_UI + +//#define DEBUG_D_ANALOG +//#define DEBUG_D_POWER +//#define DEBUG_D_UART +//#define DEBUG_D_UART_ERROR +//#define DEBUG_D_UI +//#define DEBUG_D_SOUND +//#define DEBUG_D_IIC +//#define DEBUG_D_USBDEV + +//#define ENABLE_TEST_ON_PORT4 + +//#define BUFPRINTSIZE 100000 +//#define REMEMBER_TO_ENABLE_SAVE_USER_DATA +//#define REMEMBER_TO_FIX_TYPEDATA + +//#define DEBUG_TRACE_USB_COMMAND +//#define DEBUG_TRACE_MODE_CHANGE +//#define DEBUG_TRACE_KEY +//#define DEBUG_TRACE_ANGLE +//#define DEBUG_TRACE_US +//#define DEBUG_TRACE_VM +//#define DEBUG_TRACE_DAISYCHAIN +//#define DEBUG_BYTECODE_TIME +//#define DEBUG_TRACE_FREEZE +//#define DEBUG_TRACE_FILENAME +//#define DEBUG_TRACE_IIC +//#define DEBUG_SDCARD +//#define DEBUG_USBSTICK +//#define DEBUG_VIRTUAL_BATT_TEMP +//#define DEBUG_TEMP_SHUTDOWN +//#define DEBUG_BACK_BLOCKED +//#define DEBUG_MEMORY_USAGE +#define DEBUG_RECHARGEABLE +#define ALLOW_DEBUG_PULSE + + + +#ifndef LMS2012_H_ +#define LMS2012_H_ + +// HARDWARE PLATFORM + +#define EP2 4 //!< Schematics revision D +#define FINALB 3 //!< Schematics revision B and C +#define FINAL 2 //!< Final prototype +#define SIMULATION 0 //!< LEGO digital simulation + +#define PLATFORM_START FINAL //!< Oldest supported hardware (older versions will use this) +#define PLATFORM_END EP2 //!< Newest supported hardware (newer versions will use this) + +// Will be removed when not used anymore +#define A4 -1 +#define EVALBOARD -2 +#define ONE2ONE 1 //!< First real size prototype + + +#ifdef LEGO_SIMULATION + #define HARDWARE SIMULATION +#else + +#ifndef HARDWARE + #define HARDWARE FINAL //!< Actual hardware platform (must be one of above) +#endif + +#endif + +// Support for module parameter "HwId" +// +// Readout File int PCB +// +// V1.00 10 10 MP (h = home, e = education) +// V0.50 05 5 EP3 +// V0.40 04 4 EP2 +// V0.30 03 3 EP1 (FINALB) (DEFAULT if file "HwId" not found) +// V0.20 02 2 FINAL + +#ifdef HW_ID_SUPPORT + +char *HwId = "03"; + +#define HWID (((HwId[0] - '0') * 10) + (HwId[1] - '0')) + +#endif + + +// FIRMWARE VARIANTS + +//#define OLDCALL //!< Don't use optimised sub calls +//#define DISABLE_PRU_UARTS //!< Don't use port 3 and 4 for UART sensors +//#define DISABLE_OLD_COLOR //!< Don't support NXT color sensor +//#define DISABLE_ADC //!< Don't use ADC (no clock EMC test) +//#define ADC_BITBANGING //!< Don't use SPI for a/d converter +//#define DISABLE_DAISYCHAIN +//#define DISABLE_DAISYCHAIN_COM_CALL +//#define DISABLE_FAST_DATALOG_BUFFER +//#define DISABLE_BUMBED +//#define LOG_ASCII +//#define DISABLE_FIQ_IIC +#define UART1_FAKE_INTERRUPT //!< Don't use real interrupt on UART1 (linux prompt) +//#define DISABLE_LOW_VOLTAGE //!< Don't shut down on low voltage +//#define ENABLE_HIGH_CURRENT //!< Don't shut down on high current +//#define DISABLE_LOW_MEMORY //!< Don't check low memory +//#define DISABLE_UART_DATA_ERROR //!< Don't reset UART sensor if timeout or crc error +#define DISABLE_PREEMPTED_VM //!< Don't run VM as preempted +//#define DISABLE_SDCARD_SUPPORT //!< Don't use SD card +#define DISABLE_USBSTICK_SUPPORT //!< Don't use USB stick +//#define ENABLE_PERFORMANCE_TEST //!< Show performance bar in the top line +//#define ENABLE_LOAD_TEST //!< Show integrated current in the top line +//#define ENABLE_MEMORY_TEST //!< Show used memory in the top line +//#define ENABLE_STATUS_TEST +//#define DISABLE_VIRTUAL_BATT_TEMP +//#define DISABLE_SOUND +//#define DISABLE_PAR_ALIGNMENT //!< Disable possibility to align sub call parameter types +//#define DISABLE_NEW_CALL_MUTEX //!< Disable smart object switching after return from non reentrant sub call (enables blocked thread call) +//#define DISABLE_SYSTEM_BYTECODE //!< Disable the use of opSYSTEM command +//#define DISABLE_FILENAME_CHECK //!< Disable "c_memory" filename check +//#define DISABLE_AD_WORD_PROTECT //!< Disable A/D word result protection +//#define DISABLE_UPDATE_DISASSEMBLY //!< Disable disassemble of running update commands +#define DISABLE_BLOCK_ALIAS_LOCALS //!< Disable change of block locals if sub call alias (parallelism) + +#define TESTDEVICE 3 + +#ifdef Linux_X86 +#define DISABLE_DAISYCHAIN +#define DISABLE_DAISYCHAIN_COM_CALL +#endif + +#ifndef PCASM +#include +#endif + +#include "lmstypes.h" +#include "bytecodes.h" + +// Hardware + +#define OUTPUTS vmOUTPUTS //!< Number of output ports in the system +#define INPUTS vmINPUTS //!< Number of input ports in the system +#define BUTTONS vmBUTTONS //!< Number of buttons in the system +#define LEDS vmLEDS //!< Number of LEDs in the system + +#define LCD_WIDTH vmLCD_WIDTH //!< LCD horizontal pixels +#define LCD_HEIGHT vmLCD_HEIGHT //!< LCD vertical pixels +#define TOPLINE_HEIGHT vmTOPLINE_HEIGHT //!< Top line vertical pixels +#define LCD_STORE_LEVELS vmLCD_STORE_LEVELS //!< Store levels + +// Software + +#define FG_COLOR vmFG_COLOR //!< Foreground color +#define BG_COLOR vmBG_COLOR //!< Background color + +#define CHAIN_DEPT vmCHAIN_DEPT //!< Number of bricks in the USB daisy chain (master + slaves) + +#define EVENT_BT_PIN vmEVENT_BT_PIN + +// Folders + +#define MEMORY_FOLDER vmMEMORY_FOLDER +#define PROGRAM_FOLDER vmPROGRAM_FOLDER +#define DATALOG_FOLDER vmDATALOG_FOLDER +#define SDCARD_FOLDER vmSDCARD_FOLDER +#define USBSTICK_FOLDER vmUSBSTICK_FOLDER + +// Files +#define DETAILS_FILE vmDETAILS_FILE //!< File containing firmware version + +// Extensions + +#define EXT_SOUND vmEXT_SOUND //!< Rudolf sound file +#define EXT_GRAPHICS vmEXT_GRAPHICS //!< Rudolf graphics file +#define EXT_BYTECODE vmEXT_BYTECODE //!< Rudolf byte code file +#define EXT_TEXT vmEXT_TEXT //!< Rudolf text file +#define EXT_DATALOG vmEXT_DATALOG //!< Rudolf datalog file +#define EXT_PROGRAM vmEXT_PROGRAM //!< Rudolf program byte code file +#define EXT_CONFIG vmEXT_CONFIG //!< rudolf configuration file + +/*! \page system System Configuration + * + *
+ * + * Following defines sets the system configuration and limitations.\n + * Defines with preceding "vm" is visible for the byte code assembler\n + * + * \verbatim + */ + +#define PROJECT "LMS2012" +#define VERS 1.09 + +//#define RETAILVERS //!< Retail version +//#define EDUCATIONVERS //!< Education version +#define DEVELOPERVERS //!< Developer version (telnet with password) +//#define TESTVERS '1' //!< Minor version (not shown if less than ASCII zero) + + +#ifdef RETAILVERS + +#define SPECIALVERS 'H' +#define LEGO_BUNDLE_SEED_ID "9RNK8ZF528" +#define LEGO_BUNDLE_ID "com.lego.lms" + +#endif + +#ifdef EDUCATIONVERS + +#define SPECIALVERS 'E' +#define LEGO_BUNDLE_SEED_ID "9YZJD9MXPZ" +#define LEGO_BUNDLE_ID "com.lego.education.ev3" + +#endif + +#ifdef DEVELOPERVERS + +#define SPECIALVERS 'D' +#define LEGO_BUNDLE_SEED_ID "9RNK8ZF528" +#define LEGO_BUNDLE_ID "com.lego.lms" + +#endif + +#ifdef TESTVERS + +#define SPECIALVERS TESTVERS +#define LEGO_BUNDLE_SEED_ID "BA9Q76VTQG" +#define LEGO_BUNDLE_ID "com.lego.education.ev3" +/* +#define SPECIALVERS TESTVERS +#define LEGO_BUNDLE_SEED_ID "BA9Q76VTQG" +#define LEGO_BUNDLE_ID "com.lego.edu.ev3" + +#define SPECIALVERS TESTVERS +#define LEGO_BUNDLE_SEED_ID "BA9Q76VTQG" +#define LEGO_BUNDLE_ID "com.lego.education.ev3programming" +*/ +#endif + + +#define MAX_PROGRAMS SLOTS //!< Max number of programs (including UI and direct commands) running at a time +#define MAX_BREAKPOINTS 4 //!< Max number of breakpoints (opCODES depends on this value) +#define MAX_LABELS 32 //!< Max number of labels per program +#define MAX_DEVICE_TYPE 127 //!< Highest type number (positive) +#define MAX_VALID_TYPE vmMAX_VALID_TYPE //!< Highest valid type +#define MAX_DEVICE_MODES 8 //!< Max number of modes in one device +#define MAX_DEVICE_DATASETS 8 //!< Max number of data sets in one device +#define MAX_DEVICE_DATALENGTH 32 //!< Max device data length + +#define MAX_DEVICE_BUSY_TIME 1200 //!< Max number of mS a device can be busy when read + +#define MAX_DEVICE_TYPES ((MAX_DEVICE_TYPE + 1) * MAX_DEVICE_MODES)//!< Max number of different device types and modes (max type data list size) + +#define MAX_FRAMES_PER_SEC 10 //!< Max frames per second update in display + +#define CACHE_DEEPT 10 //!< Max number of programs cached (in RECENT FILES MENU) +#define MAX_HANDLES 500 //!< Max number of handles to memory pools and arrays in one program + +#define MAX_ARRAY_SIZE 1000000000 //!< Max array size +#define MIN_ARRAY_ELEMENTS 0 //!< Min elements in a DATA8 array + +#define INSTALLED_MEMORY 6000 //!< Flash allocated to hold user programs/data +#define LOW_MEMORY 500 //!< Low memory warning [KB] + +#define LOGBUFFER_SIZE 1000 //!< Min log buffer size +#define DEVICE_LOGBUF_SIZE 300 //!< Device log buffer size (black layer buffer) +#define MIN_LIVE_UPDATE_TIME 10 //!< [mS] Min sample time when live update + +#define MIN_IIC_REPEAT_TIME 10 //!< [mS] Min IIC device repeat time +#define MAX_IIC_REPEAT_TIME 1000 //!< [mS] Max IIC device repeat time + +#define MAX_COMMAND_BYTECODES 64 //!< Max number of byte codes in a debug terminal direct command +#define MAX_COMMAND_LOCALS 64 //!< Max number of bytes allocated for direct command local variables +#define MAX_COMMAND_GLOBALS 1021 //!< Max number of bytes allocated for direct command global variables + +#define UI_PRIORITY 20 //!< UI byte codes before switching VM thread +#define C_PRIORITY 200 //!< C call byte codes + +#ifndef DISABLE_PREEMPTED_VM +#define PRG_PRIORITY 2000 //!< Prg byte codes before switching VM thread +#else +#define PRG_PRIORITY 200 //!< Prg byte codes before switching VM thread +#endif + +#define BUTTON_DEBOUNCE_TIME 30 +#define BUTTON_START_REPEAT_TIME 400 +#define BUTTON_REPEAT_TIME 200 + +#define LONG_PRESS_TIME 3000 //!< [mS] Time pressed before long press recognised + +#define ADC_REF 5000 //!< [mV] maximal value on ADC +#define ADC_RES 4095 //!< [CNT] maximal count on ADC + +#define IN1_ID_HYSTERESIS 50 //!< [mV] half of the span one Id takes up on input connection 1 voltage +#define OUT5_ID_HYSTERESIS 100 //!< [mV] half of the span one Id takes up on output connection 5 voltage + +#define DEVICE_UPDATE_TIME 1000000 //!< Min device (sensor) update time [nS] +#define DELAY_TO_TYPEDATA 10000 //!< Time from daisy chain active to upload type data [mS] +#define DAISYCHAIN_MODE_TIME 10 //!< Time for daisy chain change mode [mS] +#define MAX_FILE_HANDLES 64 //!< Max number of down load file handles +#define MIN_HANDLE 3 //!< Min file handle to close + +#define ID_LENGTH 7 //!< Id length (BT MAC id) (incl. zero terminator) +#define NAME_LENGTH 12 //!< Name length (not including zero termination) + +#define ERROR_BUFFER_SIZE 8 //!< Number of errors in buffer + +#define PWM_DEVICE "lms_pwm" //!< PWM device name +#define PWM_DEVICE_NAME "/dev/lms_pwm" //!< PWM device file name + +#define MOTOR_DEVICE "lms_motor" //!< TACHO device name +#define MOTOR_DEVICE_NAME "/dev/lms_motor" //!< TACHO device file name + +#define ANALOG_DEVICE "lms_analog" //!< ANALOG device name +#define ANALOG_DEVICE_NAME "/dev/lms_analog" //!< ANALOG device file name + +#define POWER_DEVICE "lms_power" //!< POWER device name +#define POWER_DEVICE_NAME "/dev/lms_power" //!< POWER device file name + +#define DCM_DEVICE "lms_dcm" //!< DCM device name +#define DCM_DEVICE_NAME "/dev/lms_dcm" //!< DCM device file name + +#define UI_DEVICE "lms_ui" //!< UI device name +#define UI_DEVICE_NAME "/dev/lms_ui" //!< UI device file name + +#define LCD_DEVICE "lms_display" //!< DISPLAY device name +//#define LCD_DEVICE_NAME "/dev/lms_display" //!< DISPLAY device file name +#define LCD_DEVICE_NAME "/dev/fb0" //!< DISPLAY device file name + +#define UART_DEVICE "lms_uart" //!< UART device name +#define UART_DEVICE_NAME "/dev/lms_uart" //!< UART device file name + +#define USBDEV_DEVICE "lms_usbdev" //!< USB device +#define USBDEV_DEVICE_NAME "/dev/lms_usbdev" //!< USB device + +#define USBHOST_DEVICE "lms_usbhost" //!< USB host +#define USBHOST_DEVICE_NAME "/dev/lms_usbhost" //!< USB host + +#define SOUND_DEVICE "lms_sound" //!< SOUND device name +#define SOUND_DEVICE_NAME "/dev/lms_sound" //!< SOUND device + +#define IIC_DEVICE "lms_iic" //!< IIC device name +#define IIC_DEVICE_NAME "/dev/lms_iic" //!< IIC device + +#define BT_DEVICE "lms_bt" //!< BT device name +#define BT_DEVICE_NAME "/dev/lms_bt" //!< BT device + +#define UPDATE_DEVICE "lms_update" //!< UPDATE device name +#define UPDATE_DEVICE_NAME "/dev/lms_update" //!< UPDATE device + +#define TEST_PIN_DEVICE "lms_tst_pin" //!< TEST pin device name +#define TEST_PIN_DEVICE_NAME "/dev/lms_tst_pin" //!< TEST pin device file name + +#define TEST_UART_DEVICE "lms_tst_uart" //!< TEST UART device name +#define TEST_UART_DEVICE_NAME "/dev/lms_tst_uart" //!< TEST UART device file name + + +#define DIR_DEEPT vmDIR_DEEPT //!< Max directory items allocated + +// File + +//*********************************************************************************************************************** +//! \todo Filename sizes should only use vmPATHSIZE, vmNAMESIZE, vmEXTSIZE and vmFILENAMESIZE + +#define FILENAMESIZE vmFILENAMESIZE //!< All inclusive (path, filename, extension and zero termination) +#define FILENAME_SIZE 52 //!< User filename size without extension including zero +#define FOLDERNAME_SIZE 10 //!< Folder name size relative to "lms2012" folder including zero +#define SUBFOLDERNAME_SIZE FILENAME_SIZE //!< Sub folder name size without "/" including zero + +#define MAX_FILENAME_SIZE (FOLDERNAME_SIZE + SUBFOLDERNAME_SIZE + FILENAME_SIZE + 5) + +//*********************************************************************************************************************** + +#define TYPEDATE_FILE_NAME "typedata" //!< TypeData filename +#define ICON_FILE_NAME "icon" //!< Icon image filename +#define TEXT_FILE_NAME "text" //!< Text filename + +#define DEMO_FILE_NAME "../prjs/BrkProg_SAVE/Demo.rpf" + +// Memory + + + + + +// SD card + +#ifdef Linux_X86 +#define SDCARD_DEVICE1 "/dev/mmcblk0" +#define SDCARD_DEVICE2 "/dev/mmcblk0p1" +#else +#define SDCARD_DEVICE1 "/dev/mmcblk0" +#define SDCARD_DEVICE2 "/dev/mmcblk0p1" +#endif + +#define SDCARD_MOUNT "./mount_sdcard" +#define SDCARD_UNMOUNT "./unmount_sdcard" + +// USB stick + +#ifndef Linux_X86 +#define USBSTICK_DEVICE "/dev/sda" +#else +#define USBSTICK_DEVICE "/dev/sdf1" +#endif + +#define USBSTICK_MOUNT "./mount_usbstick" +#define USBSTICK_UNMOUNT "./unmount_usbstick" + +/* +#define DEFAULT_PRJS_FOLDER "~/lms2012/prjs" //!< Project folder name without trailing "/" +#define DEFAULT_APPS_FOLDER "~/lms2012/apps" //!< Applet folder name without trailing "/" +#define DEFAULT_TOOLS_FOLDER "~/lms2012/tools" //!< Setting folder name without trailing "/" +#define DEFAULT_SYS_FOLDER "~/lms2012/sys" //!< System folder name without trailing "/" +#define DEFAULT_SETUP_FOLDER "~/lms2012/sys" //!< Setup folder name without trailing "/" +#define DEFAULT_SD_FOLDER "~/lms2012/SD Card" //!< Sd card folder name without trailing "/" +#define DEFAULT_USB_FOLDER "~/lms2012/USB Memory"//!< USB Memory folder name without trailing "/" +*/ + +#define DEFAULT_FOLDER "ui" //!< Folder containing the first small programs +#define DEFAULT_UI "ui" //!< Default user interface + +#define DEFAULT_VOLUME vmDEFAULT_VOLUME +#define DEFAULT_SLEEPMINUTES vmDEFAULT_SLEEPMINUTES + +#define COM_CMD_DEVICE_NAME USBDEV_DEVICE_NAME //!< USB HID command pipe device file name + +/*! \endverbatim + * + */ + +/*! \page powermanagement Power Management + * + * This section describes various constants used in the power management + * + * + *\n + *
+ * Battery Indicator + *\n + *
+ * \verbatim + */ + +#define BATT_INDICATOR_HIGH 7500 //!< Battery indicator high [mV] +#define BATT_INDICATOR_LOW 6200 //!< Battery indicator low [mV] + +#define ACCU_INDICATOR_HIGH 7500 //!< Rechargeable battery indicator high [mV] +#define ACCU_INDICATOR_LOW 7100 //!< Rechargeable battery indicator low [mV] + +/*! \endverbatim + * \subpage pmbattind + *\n + *
+ * Low Voltage Shutdown + *\n + *
+ * \verbatim + */ + +#define LOW_VOLTAGE_SHUTDOWN_TIME 10000 //!< Time from shutdown lower limit to shutdown [mS] + +#define BATT_WARNING_HIGH 6.2 //!< Battery voltage warning upper limit [V] +#define BATT_WARNING_LOW 5.5 //!< Battery voltage warning lower limit [V] +#define BATT_SHUTDOWN_HIGH 5.5 //!< Battery voltage shutdown upper limit [V] +#define BATT_SHUTDOWN_LOW 4.5 //!< Battery voltage shutdown lower limit [V] + +#define ACCU_WARNING_HIGH 7.1 //!< Rechargeable battery voltage warning upper limit [V] +#define ACCU_WARNING_LOW 6.5 //!< Rechargeable battery voltage warning lower limit [V] +#define ACCU_SHUTDOWN_HIGH 6.5 //!< Rechargeable battery voltage shutdown upper limit [V] +#define ACCU_SHUTDOWN_LOW 6.0 //!< Rechargeable battery voltage shutdown lower limit [V] + + /*! \endverbatim + * \subpage pmbattsd + *\n + */ +#ifdef ENABLE_HIGH_CURRENT + /*! \page powermanagement + * + *
+ * High Load Shutdown + *\n + *
+ * \verbatim + */ + +#define LOAD_SHUTDOWN_FAIL 4.0 //!< Current limit for instantly shutdown [A] +#define LOAD_SHUTDOWN_HIGH 3.0 //!< Current limit for integrated current shutdown [A*S] +#define LOAD_BREAK_EVEN 1.75 //!< Current limit for integrated current break even [A*S] + +#define LOAD_SLOPE_UP 0.2 //!< Current slope when current is above break even [A/S] +#define LOAD_SLOPE_DOWN 0.1 //!< Current slope when current is below break even [A/S] + + /*! \endverbatim + * \subpage pmloadsd + *\n + */ +#endif + +#ifndef DISABLE_VIRTUAL_BATT_TEMP + /*! \page powermanagement + * + *
+ * High Temperature Shutdown + *\n + *
+ * \verbatim + */ + +#define TEMP_SHUTDOWN_FAIL 45.0 //!< Temperature rise before fail shutdown [C] +#define TEMP_SHUTDOWN_WARNING 40.0 //!< Temperature rise before warning [C] + + /*! \endverbatim + * \subpage pmtempsd + *\n + */ +#endif + +#define UPDATE_TIME1 2 //!< Update repeat time1 [mS] +#define UPDATE_TIME2 10 //!< Update repeat time2 [mS] +#define UPDATE_MEMORY 200 //!< Update memory size [mS] +#define UPDATE_SDCARD 500 //!< Update sdcard size [mS] +#define UPDATE_USBSTICK 500 //!< Update usbstick size [mS] + + +// Per start of (polution) defines +#define MAX_SOUND_DATA_SIZE 250 +#define SOUND_CHUNK 250 +#define SOUND_ADPCM_CHUNK 125 +#define SOUND_MASTER_CLOCK 132000000 +#define SOUND_TONE_MASTER_CLOCK 1031250 +#define SOUND_MIN_FRQ 250 +#define SOUND_MAX_FRQ 10000 +#define SOUND_MAX_LEVEL 8 +#define SOUND_FILE_BUFFER_SIZE SOUND_CHUNK + 2 // 12.8 mS @ 8KHz +#define SOUND_BUFFER_COUNT 3 +#define SOUND_FILE_FORMAT_NORMAL 0x0100 // RSO-file +#define SOUND_FILE_FORMAT_COMPRESSED 0x0101 // RSO-file compressed +// Per end of defines + + +/*! + * + * \n + */ + + +#define VtoC(V) ((UWORD)((V * ADC_RES) / ADC_REF)) +#define CtoV(C) ((UWORD)((C * ADC_REF) / ADC_RES)) +#define MtoV(M) ((UWORD)((M * ADC_REF * 100) / (ADC_RES * 52))) + +#define KB 1024 + +enum +{ + FALSE = 0, + TRUE = 1, +}; + + +/*! \page connections Connections + * + * \anchor connectiontypes + * + * Following defines sets the input and output port connection type\n + * + * These connection types are evaluated in the DCM driver \ref DcmDriver "Device Detection Manager Driver" + * + * \verbatim + */ + +typedef enum +{ + CONN_UNKNOWN = 111, //!< Connection is fake (test) + + CONN_DAISYCHAIN = 117, //!< Connection is daisy chained + CONN_NXT_COLOR = 118, //!< Connection type is NXT color sensor + CONN_NXT_DUMB = 119, //!< Connection type is NXT analog sensor + CONN_NXT_IIC = 120, //!< Connection type is NXT IIC sensor + + CONN_INPUT_DUMB = 121, //!< Connection type is LMS2012 input device with ID resistor + CONN_INPUT_UART = 122, //!< Connection type is LMS2012 UART sensor + + CONN_OUTPUT_DUMB = 123, //!< Connection type is LMS2012 output device with ID resistor + CONN_OUTPUT_INTELLIGENT = 124, //!< Connection type is LMS2012 output device with communication + CONN_OUTPUT_TACHO = 125, //!< Connection type is LMS2012 tacho motor with series ID resistance + + CONN_NONE = 126, //!< Port empty or not available + CONN_ERROR = 127, //!< Port not empty and type is invalid +} +CONN; + +/*\endverbatim + * + * \n + */ + + + +/*! \page objectstatus Object Status + * +\verbatim + +Image load -> Initialize all objects + + +Initialize -> if TriggerCount == 0 -> RUNNING (VMTHREAD) + else -> STOPPED (waiting for 1. trigger) + +1.Trigger -> Initialise -> WAITING + +Triggered -> Enqueue -> RUNNING + +Done -> Dequeue -> STOPPED + + +Program start + | + v + STOPPED -------> WAITING -------> RUNNING --------, + ^ 1.trig/ n.trig/ done/ | + | Reset+Enqueue Dequeue | + | | + '----------------------------------------------' + +\endverbatim + */ + + +/*! \enum DSPSTAT + * + * Dispatch status values + */ +typedef enum +{ + NOBREAK = 0x0100, //!< Dispatcher running (looping) + STOPBREAK = 0x0200, //!< Break because of program stop + SLEEPBREAK = 0x0400, //!< Break because of sleeping + INSTRBREAK = 0x0800, //!< Break because of opcode break + BUSYBREAK = 0x1000, //!< Break because of waiting for completion + PRGBREAK = 0x2000, //!< Break because of program break + USERBREAK = 0x4000, //!< Break because of user decision + FAILBREAK = 0x8000 //!< Break because of fail +} +DSPSTAT; + +typedef void (*PRIM)(void); //!< Prototype for all byte codes + + + +/*! \page memorylayout Memory Layout + * + * RAM layout + * + *- GlobalVariables (aligned) + * + *- ObjectPointerList (aligned) + * + *- OBJ (aligned) + * - Ip (4 bytes) + * - Status (2 bytes) + * - TriggerCount/CallerId (2 bytes) + * - Local (0..MAX Bytes)\n + * + */ + +/*! \struct OBJ + * Object data is used to hold the variables used for an object (allocated at image load time) + */ +typedef struct // Object +{ + IP Ip; //!< Object instruction pointer + LP pLocal; //!< Local variable pointer +#ifndef DISABLE_NEW_CALL_MUTEX + UBYTE ObjStatus; //!< Object status + UBYTE Blocked; +#else + UWORD ObjStatus; //!< Object status +#endif + union //!< Different meaning for SUBCALL and BLOCKS + { + OBJID CallerId; //!< Caller id used for SUBCALL to save object id to return to + TRIGGER TriggerCount; //!< Trigger count used by BLOCK's trigger logic + }u; + VARDATA Local[]; //!< Poll of bytes used for local variables +} +OBJ; + + +/*! \struct BRKP + * Breakpoint data hold information used for breakpoint + */ +typedef struct +{ + IMINDEX Addr; //!< Offset to breakpoint address from image start + OP OpCode; //!< Saved substituted opcode +} +BRKP; + +/*! \struct PRG + * Program data hold information about a program + */ +typedef struct +{ + ULONG InstrCnt; //!< Instruction counter used for performance analyses + ULONG InstrTime; //!< Instruction time used for performance analyses + + ULONG StartTime; //!< Program start time [mS] + ULONG RunTime; //!< Program run time [uS] + + IP pImage; //!< Pointer to start of image + GP pData; //!< Pointer to start of data + GP pGlobal; //!< Pointer to start of global bytes + OBJHEAD* pObjHead; //!< Pointer to start of object headers + OBJ** pObjList; //!< Pointer to object pointer list + IP ObjectIp; //!< Working object Ip + LP ObjectLocal; //!< Working object locals + + OBJID Objects; //!< No of objects in image + OBJID ObjectId; //!< Active object id + + OBJSTAT Status; //!< Program status + OBJSTAT StatusChange; //!< Program status change + RESULT Result; //!< Program result (OK, BUSY, FAIL) + + BRKP Brkp[MAX_BREAKPOINTS]; //!< Storage for breakpoint logic + + LABEL Label[MAX_LABELS]; //!< Storage for labels + UWORD Debug; //!< Debug flag + + DATA8 Name[FILENAME_SIZE]; + +} +PRG; + + +#define TYPE_NAME_LENGTH 11 +#define SYMBOL_LENGTH 4 //!< Symbol leng th (not including zero) + +/*! \struct TYPES + * Device type data + */ +typedef struct // if data type changes - remember to change "cInputTypeDataInit" ! +{ + SBYTE Name[TYPE_NAME_LENGTH + 1]; //!< Device name + DATA8 Type; //!< Device type + DATA8 Connection; + DATA8 Mode; //!< Device mode + DATA8 DataSets; + DATA8 Format; + DATA8 Figures; + DATA8 Decimals; + DATA8 Views; + DATAF RawMin; //!< Raw minimum value (e.c. 0.0) + DATAF RawMax; //!< Raw maximum value (e.c. 1023.0) + DATAF PctMin; //!< Percent minimum value (e.c. -100.0) + DATAF PctMax; //!< Percent maximum value (e.c. 100.0) + DATAF SiMin; //!< SI unit minimum value (e.c. -100.0) + DATAF SiMax; //!< SI unit maximum value (e.c. 100.0) + UWORD InvalidTime; //!< mS from type change to valid data + UWORD IdValue; //!< Device id value (e.c. 0 ~ UART) + DATA8 Pins; //!< Device pin setup + SBYTE Symbol[SYMBOL_LENGTH + 1]; //!< SI unit symbol + UWORD Align; +} +TYPES; + +#define TYPE_PARAMETERS 19 //!< Number of members in the structure above +#define MAX_DEVICE_INFOLENGTH 54 //!< Number of bytes in the structure above (can not be changed) + + +/*!\enum ERR + * + * Describes error numbers + */ +typedef enum +{ + TOO_MANY_ERRORS_TO_BUFFER, + TYPEDATA_TABEL_FULL, + TYPEDATA_FILE_NOT_FOUND, + ANALOG_DEVICE_FILE_NOT_FOUND, + ANALOG_SHARED_MEMORY, + UART_DEVICE_FILE_NOT_FOUND, + UART_SHARED_MEMORY, + IIC_DEVICE_FILE_NOT_FOUND, + IIC_SHARED_MEMORY, + DISPLAY_SHARED_MEMORY, + UI_SHARED_MEMORY, + UI_DEVICE_FILE_NOT_FOUND, + LCD_DEVICE_FILE_NOT_FOUND, + OUTPUT_SHARED_MEMORY, + COM_COULD_NOT_OPEN_FILE, + COM_NAME_TOO_SHORT, + COM_NAME_TOO_LONG, + COM_INTERNAL, + VM_INTERNAL, + VM_PROGRAM_VALIDATION, + VM_PROGRAM_NOT_STARTED, + VM_PROGRAM_FAIL_BREAK, + VM_PROGRAM_INSTRUCTION_BREAK, + VM_PROGRAM_NOT_FOUND, + SOUND_DEVICE_FILE_NOT_FOUND, + SOUND_SHARED_MEMORY, + FILE_OPEN_ERROR, + FILE_READ_ERROR, + FILE_WRITE_ERROR, + FILE_CLOSE_ERROR, + FILE_GET_HANDLE_ERROR, + FILE_NAME_ERROR, + USB_SHARED_MEMORY, + OUT_OF_MEMORY, + ERRORS +} +ERR; + +// INTERFACE FOR SHARED LIBRARIES + +extern void PrimParAdvance(void); // Dummy get parameter + +extern void* PrimParPointer(void); // Get pointer to primitives and system calls parameters + +extern IP GetImageStart(void); // Get pointer to start of image + +extern void SetDispatchStatus(DSPSTAT Status); // Set dispatch status (result from executing byte code) + +extern void SetInstructions(ULONG Instructions); // Set number of instructions before VMThread change + +extern PRGID CurrentProgramId(void); // Get current program id + +extern OBJSTAT ProgramStatus(PRGID PrgId); // Get program status + +extern OBJSTAT ProgramStatusChange(PRGID PrgId); // Get program status change + +extern void ProgramEnd(PRGID PrgId); + +extern OBJID CallingObjectId(void); // Get calling objects id + +extern void AdjustObjectIp(IMOFFS Value); // Adjust IP + +extern IP GetObjectIp(void); // Get IP + +extern void SetObjectIp(IP Ip); // Set IP + +extern ULONG GetTimeUS(void); // Get uS + +extern ULONG GetTimeMS(void); // Get mS + +extern ULONG GetTime(void); // Get actual program time + +extern ULONG CurrentObjectIp(void); // Get current object ip + +extern void VmPrint(char *pString); // print string + +extern void SetTerminalEnable(DATA8 Value); // Terminal enable/disable + +extern DATA8 GetTerminalEnable(void); // Get terminal enable state + +extern void GetResourcePath(char *pString,DATA8 MaxLength);// Get resource path + +extern void* VmMemoryResize(HANDLER Handle,DATA32 Elements); + +extern void SetVolumePercent(DATA8 Volume); + +extern DATA8 GetVolumePercent(void); + +extern void SetSleepMinutes(DATA8 Minutes); + +extern DATA8 GetSleepMinutes(void); + +extern DSPSTAT ExecuteByteCode(IP pByteCode,GP pGlobals,LP pLocals); // Execute byte code stream (C-call) + +extern DATA8 CheckSdcard(DATA8 *pChanged,DATA32 *pTotal,DATA32 *pFree,DATA8 Force); + +extern DATA8 CheckUsbstick(DATA8 *pChanged,DATA32 *pTotal,DATA32 *pFree,DATA8 Force); + +extern void SetUiUpdate(void); + +extern RESULT ValidateChar(DATA8 *pChar,DATA8 Set); + +extern RESULT ValidateString(DATA8 *pString,DATA8 Set); + +extern ERR LogErrorGet(void); + +#ifdef BUFPRINTSIZE +extern void BufPrint(char Cmd,char *pFormat, ...); +#endif + + + +#define ERR_STRING_SIZE vmERR_STRING_SIZE // Inclusive zero termination + +extern void LogErrorNumber(ERR Error); // Log error number +extern DATA8 LogErrorNumberExists(ERR Error); + + +#ifndef DISABLE_OLD_COLOR + +#define COLORS 4 +#define CALPOINTS 3 + + +/*! \page NxtColorMemory + * + * Shared Memory + * + *
+ * + * + * \verbatim + */ + +typedef struct +{ + ULONG Calibration[CALPOINTS][COLORS]; + UWORD CalLimits[CALPOINTS - 1]; + UWORD Crc; + UWORD ADRaw[COLORS]; + UWORD SensorRaw[COLORS]; +} +COLORSTRUCT; + +/*\endverbatim + * + * \n + */ + +#endif + + +// INTERFACE BETWEEN SHARED LIBRARIES AND MODULES + +/*! \page AnalogModuleMemory + * Shared Memory + * + *
+ * + * It is possible to get a pointer to the raw analogue values for use in userspace + * this pointer will point to a struct and the layout is following: + * + * \verbatim + */ + +typedef struct +{ + DATA16 InPin1[INPUTS]; //!< Analog value at input port connection 1 + DATA16 InPin6[INPUTS]; //!< Analog value at input port connection 6 + DATA16 OutPin5[OUTPUTS]; //!< Analog value at output port connection 5 + DATA16 BatteryTemp; //!< Battery temperature + DATA16 MotorCurrent; //!< Current flowing to motors + DATA16 BatteryCurrent; //!< Current flowing from the battery + DATA16 Cell123456; //!< Voltage at battery cell 1, 2, 3,4, 5, and 6 +#ifndef DISABLE_FAST_DATALOG_BUFFER + DATA16 Pin1[INPUTS][DEVICE_LOGBUF_SIZE]; //!< Raw value from analog device + DATA16 Pin6[INPUTS][DEVICE_LOGBUF_SIZE]; //!< Raw value from analog device + UWORD Actual[INPUTS]; + UWORD LogIn[INPUTS]; + UWORD LogOut[INPUTS]; +#endif +#ifndef DISABLE_OLD_COLOR + COLORSTRUCT NxtCol[INPUTS]; +#endif + DATA16 OutPin5Low[OUTPUTS]; //!< Analog value at output port connection 5 when connection 6 is low + + DATA8 Updated[INPUTS]; + + DATA8 InDcm[INPUTS]; //!< Input port device types + DATA8 InConn[INPUTS]; + + DATA8 OutDcm[OUTPUTS]; //!< Output port device types + DATA8 OutConn[OUTPUTS]; +#ifndef DISABLE_PREEMPTED_VM + UWORD PreemptMilliSeconds; +#endif +} +ANALOG; + +/*\endverbatim + * + * \n + */ + + +/*! \page UartModuleMemory + * + * Shared Memory + * + *
+ * + * It is possible to get a pointer to the uart values for use in userspace + * this pointer will point to a struct and the layout is following: + * + * \verbatim + */ + +#define UART_DATA_LENGTH MAX_DEVICE_DATALENGTH +#define UART_BUFFER_SIZE 64 + +typedef struct +{ + TYPES TypeData[INPUTS][MAX_DEVICE_MODES]; //!< TypeData + +#ifndef DISABLE_FAST_DATALOG_BUFFER + UWORD Repeat[INPUTS][DEVICE_LOGBUF_SIZE]; + DATA8 Raw[INPUTS][DEVICE_LOGBUF_SIZE][UART_DATA_LENGTH]; //!< Raw value from UART device + UWORD Actual[INPUTS]; + UWORD LogIn[INPUTS]; +#else + DATA8 Raw[INPUTS][UART_DATA_LENGTH]; //!< Raw value from UART device +#endif + DATA8 Status[INPUTS]; //!< Status + DATA8 Output[INPUTS][UART_DATA_LENGTH]; //!< Bytes to UART device + DATA8 OutputLength[INPUTS]; +} +UART; + +/*\endverbatim + * + * \n + */ + + +#define UART_PORT_CHANGED 0x01 //!< Input port changed +#define UART_DATA_READY 0x08 //!< Data is ready +#define UART_WRITE_REQUEST 0x10 //!< Write request + + +typedef struct +{ + DATA8 Connection[INPUTS]; + DATA8 Type[INPUTS]; + DATA8 Mode[INPUTS]; +} +DEVCON; + + +typedef struct +{ + TYPES TypeData; + DATA8 Port; + DATA8 Mode; +} +UARTCTL; + +#define UART_SET_CONN _IOWR('u',0,DEVCON) +#define UART_READ_MODE_INFO _IOWR('u',1,UARTCTL) +#define UART_NACK_MODE_INFO _IOWR('u',2,UARTCTL) +#define UART_CLEAR_CHANGED _IOWR('u',3,UARTCTL) + + +/*! \page IicModuleMemory + * + * Shared Memory + * + *
+ * + * It is possible to get a pointer to the iic values for use in userspace + * this pointer will point to a struct and the layout is following: + * + * \verbatim + */ + +#define IIC_DATA_LENGTH MAX_DEVICE_DATALENGTH +#define IIC_NAME_LENGTH 8 + +typedef struct +{ + TYPES TypeData[INPUTS][MAX_DEVICE_MODES]; //!< TypeData + +#ifndef DISABLE_FAST_DATALOG_BUFFER + UWORD Repeat[INPUTS][DEVICE_LOGBUF_SIZE]; + DATA8 Raw[INPUTS][DEVICE_LOGBUF_SIZE][IIC_DATA_LENGTH]; //!< Raw value from IIC device + UWORD Actual[INPUTS]; + UWORD LogIn[INPUTS]; +#else + DATA8 Raw[INPUTS][IIC_DATA_LENGTH]; //!< Raw value from IIC device +#endif + DATA8 Status[INPUTS]; //!< Status + DATA8 Changed[INPUTS]; + DATA8 Output[INPUTS][IIC_DATA_LENGTH]; //!< Bytes to IIC device + DATA8 OutputLength[INPUTS]; +} +IIC; + +/*\endverbatim + * + * \n + */ + + +#define IIC_PORT_CHANGED 0x01 //!< Input port changed +#define IIC_DATA_READY 0x08 //!< Data is ready +#define IIC_WRITE_REQUEST 0x10 //!< Write request + + +typedef struct +{ + TYPES TypeData; + DATA8 Port; + DATA8 Mode; +} +IICCTL; + + +typedef struct +{ + RESULT Result; + DATA8 Port; + DATA8 Repeat; + DATA16 Time; + DATA8 WrLng; + DATA8 WrData[IIC_DATA_LENGTH]; + DATA8 RdLng; + DATA8 RdData[IIC_DATA_LENGTH]; +} +IICDAT; + + +typedef struct +{ + DATA8 Port; + DATA16 Time; + DATA8 Type; + DATA8 Mode; + DATA8 Manufacturer[IIC_NAME_LENGTH + 1]; + DATA8 SensorType[IIC_NAME_LENGTH + 1]; + DATA8 SetupLng; + ULONG SetupString; + DATA8 PollLng; + ULONG PollString; + DATA8 ReadLng; +} +IICSTR; + + +#define IIC_SET_CONN _IOWR('i',2,DEVCON) +#define IIC_READ_TYPE_INFO _IOWR('i',3,IICCTL) +#define IIC_SETUP _IOWR('i',5,IICDAT) +#define IIC_SET _IOWR('i',6,IICSTR) + + + + +#define TST_PIN_LENGTH 8 + +typedef struct +{ + DATA8 Port; + DATA8 Length; + DATA8 String[TST_PIN_LENGTH + 1]; +} +TSTPIN; + +#define TST_PIN_ON _IOWR('t',1,TSTPIN) +#define TST_PIN_OFF _IOWR('t',2,TSTPIN) +#define TST_PIN_READ _IOWR('t',3,TSTPIN) +#define TST_PIN_WRITE _IOWR('t',4,TSTPIN) + + + + +#define TST_UART_LENGTH UART_BUFFER_SIZE + +typedef struct +{ + DATA32 Bitrate; + DATA8 Port; + DATA8 Length; + DATA8 String[TST_UART_LENGTH]; +} +TSTUART; + +#define TST_UART_ON _IOWR('t',5,TSTUART) +#define TST_UART_OFF _IOWR('t',6,TSTUART) +#define TST_UART_EN _IOWR('t',7,TSTUART) +#define TST_UART_DIS _IOWR('t',8,TSTUART) +#define TST_UART_READ _IOWR('t',9,TSTUART) +#define TST_UART_WRITE _IOWR('t',10,TSTUART) + + + + +/*! \page UiModuleMemory + * + * Shared Memory + * + *
+ * + * It is possible to get a pointer to the ui button states for use in userspace + * this pointer will point to a struct and the layout is following: + * + * \verbatim + */ + +typedef struct +{ + DATA8 Pressed[BUTTONS]; //!< Pressed status +} +UI; + +/*\endverbatim + * + * \n + */ + + +#define LCD_BUFFER_SIZE (((LCD_WIDTH + 7) / 8) * LCD_HEIGHT) +#define LCD_TOPLINE_SIZE (((LCD_WIDTH + 7) / 8) * (TOPLINE_HEIGHT + 1)) + + +/*! \page DisplayModuleMemory + * + * Shared Memory + * + *
+ * + * It is possible to get a pointer to the display memory for use in userspace + * this pointer will point to a frame buffer as follows: + * + * \verbatim + */ + +typedef struct +{ + UBYTE Lcd[LCD_BUFFER_SIZE]; +} +LCD; + +/*\endverbatim + * + * \n + */ + + +/*! \page SoundModuleMemory + * + * Shared Memory + * + *
+ * + * It is possible to get a pointer to the sound ready flag for use in userspace + * this pointer will point to a struct and the layout is following: + * + * \verbatim + */ + +typedef struct +{ + DATA8 Status; //!< Status +} +SOUND; + +/*\endverbatim + * + * \n + */ + + +/*! \page UsbModuleMemory + * + * Shared Memory + * + *
+ * + * It is possible to get a pointer to the USB Speed (FULL or HIGH) for use in userspace + * this pointer will point to a struct and the layout is following: + * + * \verbatim + */ + +enum { + FULL_SPEED, + HIGH_SPEED + }; + +typedef struct +{ + DATA8 Speed; +} +USB_SPEED; + +/*\endverbatim + * + * \n + */ + +/*! \page VmNonvolatileMemory + * + * VM non volatile Memory + * + *
+ * + * This struct will be loaded at start up and save when closing down VM + * + * \verbatim + */ + +typedef struct +{ + DATA8 VolumePercent; //!< System default volume [0..100%] + DATA8 SleepMinutes; //!< System sleep [0..120min] (0 = ~) +} +NONVOL; + +/*\endverbatim + * + * \n + */ + + +/* + * Motor/OUTPUT Typedef + */ +typedef struct +{ + SLONG TachoCounts; + SBYTE Speed; + SLONG TachoSensor; +}MOTORDATA; + +typedef struct +{ + DATA8 Cmd; + DATA8 Nos; + DATA8 Power; + DATA32 Step1; + DATA32 Step2; + DATA32 Step3; + DATA8 Brake; +} STEPPOWER; + +typedef struct +{ + DATA8 Cmd; + DATA8 Nos; + DATA8 Power; + DATA32 Time1; + DATA32 Time2; + DATA32 Time3; + DATA8 Brake; +} TIMEPOWER; + +typedef struct +{ + DATA8 Cmd; + DATA8 Nos; + DATA8 Speed; + DATA32 Step1; + DATA32 Step2; + DATA32 Step3; + DATA8 Brake; +} STEPSPEED; + +typedef struct +{ + DATA8 Cmd; + DATA8 Nos; + DATA8 Speed; + DATA32 Time1; + DATA32 Time2; + DATA32 Time3; + DATA8 Brake; +} TIMESPEED; + +typedef struct +{ + DATA8 Cmd; + DATA8 Nos; + DATA8 Speed; + DATA16 Turn; + DATA32 Step; + DATA8 Brake; +} STEPSYNC; + +typedef struct +{ + DATA8 Cmd; + DATA8 Nos; + DATA8 Speed; + DATA16 Turn; + DATA32 Time; + DATA8 Brake; +} TIMESYNC; +/* + * End of Motor/OUTPUT Typedef + */ + + +#define PRINTBUFFERSIZE 160 + +typedef struct +{ + NONVOL NonVol; + DATA8 FirstProgram[MAX_FILENAME_SIZE]; + + char PrintBuffer[PRINTBUFFERSIZE + 1]; + DATA8 TerminalEnabled; + + PRGID FavouritePrg; + PRGID ProgramId; //!< Program id running + PRG Program[MAX_PROGRAMS]; //!< Program[0] is the UI byte codes running + + + ULONG InstrCnt; //!< Instruction counter (performance test) + IP pImage; //!< Pointer to start of image + GP pGlobal; //!< Pointer to start of global bytes + OBJHEAD* pObjHead; //!< Pointer to start of object headers + OBJ** pObjList; //!< Pointer to object pointer list + + IP ObjectIp; //!< Working object Ip + LP ObjectLocal; //!< Working object locals + OBJID Objects; //!< No of objects in image + OBJID ObjectId; //!< Active object id + + IP ObjIpSave; + GP ObjGlobalSave; + LP ObjLocalSave; + DSPSTAT DispatchStatusSave; + ULONG PrioritySave; + + long TimerDataSec; + long TimerDatanSec; + + UWORD Debug; + + UWORD Test; + + UWORD RefCount; + + ULONG TimeuS; + + ULONG OldTime1; + ULONG OldTime2; + ULONG NewTime; +#ifdef ENABLE_PERFORMANCE_TEST + ULONG PerformTimer; + DATAF PerformTime; +#endif + + DSPSTAT DispatchStatus; //!< Dispatch status + ULONG Priority; //!< Object priority + + ULONG Value; + HANDLER Handle; + + ERR Errors[ERROR_BUFFER_SIZE]; + UBYTE ErrorIn; + UBYTE ErrorOut; + + DATA32 MemorySize; + DATA32 MemoryFree; + ULONG MemoryTimer; + +#ifndef DISABLE_SDCARD_SUPPORT + DATA32 SdcardSize; + DATA32 SdcardFree; + ULONG SdcardTimer; + DATA8 SdcardOk; +#endif + +#ifndef DISABLE_USBSTICK_SUPPORT + DATA32 UsbstickSize; + DATA32 UsbstickFree; + ULONG UsbstickTimer; + DATA8 UsbstickOk; +#endif + + LCD LcdBuffer; //!< Copy of last LCD update + DATA8 LcdUpdated; //!< LCD updated + + ANALOG Analog; + ANALOG *pAnalog; + int AdcFile; + +#ifdef ENABLE_STATUS_TEST + DATA8 Status; +#endif + +#ifdef ALLOW_DEBUG_PULSE + DATA8 PulseShow; + DATA8 Pulse; +#endif + +#if (HARDWARE == SIMULATION) + class NXT * nxt; +#endif +}GLOBALS; + + +#if (HARDWARE == SIMULATION) + extern GLOBALS * gInstance; + #define VMInstance (*gInstance) + RESULT mSchedInit(); + RESULT mSchedCtrl(); + RESULT mSchedExit(); + + void setInstance(GLOBALS * _Instance); + GLOBALS * getInstance(); +#else + extern GLOBALS VMInstance; +#endif + + +#endif /* LMS2012_H_ */ diff --git a/brick/kernel/source/lmstypes.h b/brick/kernel/source/lmstypes.h new file mode 100644 index 00000000..390ed6bd --- /dev/null +++ b/brick/kernel/source/lmstypes.h @@ -0,0 +1,195 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * As a special exception, if other files instantiate templates or use macros or + * inline functions from this file, or you compile this file and link it with + * other works to produce a work based on this file, this file does not by itself + * cause the resulting work to be covered by the GNU General Public License. + * However the source code for this file must still be made available in accordance + * with section (3) of the GNU General Public License. + * + */ + + +#ifndef LMSTYPES_H_ +#define LMSTYPES_H_ + +// BASIC DATA TYPES + +#ifndef LEGO_SIMULATION + +typedef unsigned char UBYTE; //!< Basic Type used to symbolise 8 bit unsigned values +typedef unsigned short UWORD; //!< Basic Type used to symbolise 16 bit unsigned values +typedef unsigned int ULONG; //!< Basic Type used to symbolise 32 bit unsigned values + +typedef signed char SBYTE; //!< Basic Type used to symbolise 8 bit signed values +typedef signed short SWORD; //!< Basic Type used to symbolise 16 bit signed values +typedef signed int SLONG; //!< Basic Type used to symbolise 32 bit signed values + +typedef float FLOAT; //!< Basic Type used to symbolise 32 bit floating point values + +#define LFILE FILE + +#else + +#include +#include + +typedef LEGO::UInt8 UBYTE; //!< Basic Type used to symbolise 8 bit unsigned values +typedef LEGO::UInt16 UWORD; //!< Basic Type used to symbolise 16 bit unsigned values +typedef unsigned long ULONG; //!< Basic Type used to symbolise 32 bit unsigned values + + +typedef LEGO::Int8 SBYTE; //!< Basic Type used to symbolise 8 bit signed values +typedef LEGO::Int16 SWORD; //!< Basic Type used to symbolise 16 bit signed values +typedef LEGO::Int32 SLONG; //!< Basic Type used to symbolise 32 bit signed values + +typedef LEGO::Real32 FLOAT; //!< Basic Type used to symbolise 32 bit floating point values + +#endif + +// VM DATA TYPES + +typedef SBYTE DATA8; //!< VM Type for 1 byte signed value +typedef SWORD DATA16; //!< VM Type for 2 byte signed value +typedef SLONG DATA32; //!< VM Type for 4 byte signed value +typedef FLOAT DATAF; //!< VM Type for 4 byte floating point value + +// VM VARIABLE TYPES + +typedef UBYTE VARDATA; //!< Variable base type +typedef UBYTE IMGDATA; //!< Image base type + +typedef UWORD PRGID; //!< Program id type + +typedef UWORD OBJID; //!< Object id type +typedef IMGDATA* IP; //!< Instruction pointer type +typedef VARDATA* LP; //!< Local variable pointer type +typedef VARDATA* GP; //!< global variable pointer type + +typedef ULONG IMINDEX; //!< ImageData index type +typedef ULONG GBINDEX; //!< GlobalBytes index type +typedef ULONG LBINDEX; //!< LocalBytes index type +typedef UWORD TRIGGER; //!< TriggerCount type +typedef UBYTE PARS; //!< NoOfParameters type +typedef SLONG IMOFFS; //!< ImageData offset type + +typedef DATA16 HANDLER; //!< Memory list index + + + +/*! \page imagelayout Image Layout + * The image consists of three different components in this fixed order: imageheader, objectheaders and byte codes. + * + * The imageheader tells something about image version, filesize, no of objectheaders (objects) and no of global variable bytes. + * + * + * Objectheaders contains different informations depending on the nature of the object: + * + *- The VMTHREAD object (former TBC_TOPVI) \n + * - OffsetToInstructions tells were to find the corresponding byte codes (offset from image start) \n + * - OwnerObjectId must be zero \n + * - TriggerCount is used but must be zero \n + * - LocalBytes describes the number of bytes for local variables \n + * + *- The SUBCALL object (former TBC_VI and TBC_VI_ALIAS) \n + * - OffsetToInstructions tells were to find the corresponding byte codes (if alias this is equal to mother object) \n + * - OwnerObjectId must be zero \n + * - TriggerCount is used and must be one \n + * - LocalBytes describes the number of bytes for local variables \n + * + *- The BLOCK object (former CLUMP) \n + * - OffsetToInstructions tells were to find the corresponding byte codes (offset from image start) \n + * - OwnerObjectId is equal to object id it belongs to (not equal to zero) \n + * - TriggerCount is used to determine how many triggers needed before the BLOCK object is activated \n + * - LocalBytes must be zero (locals are defined in the owner object) \n + * + * Byte codes are described in a different section. + * + * Little Endian are used (addresses and data are represented with LSB on lowest address and MSB on highest address). + * + * Offset to instructions is number of bytes from start of image to start of object instructions. + * + * Index to global variables are byte based and counted from start of globals (zero based). + * + * Index to local variables are byte based and counted from start of object locals (zero based). + * + * Object ID's is not zero based - First object (VMTHEAD) is named 1. + * + */ + +/*! \page imagelayout + * + * FILE layout (aligned) + * + *- IMGHEAD (aligned) + * - Sign (4 bytes) + * - ImageSize (4 bytes) + * - VersionInfo (2 bytes) + * - NumberOfObjects (2 bytes) + * - GlobalBytes (4 bytes) + */ + +/*! \struct IMGHEAD + * Image header + */ +typedef struct +{ + UBYTE Sign[4]; //!< Place holder for the file type identifier + IMINDEX ImageSize; //!< Image size + UWORD VersionInfo; //!< Version identifier + OBJID NumberOfObjects; //!< Total number of objects in image + GBINDEX GlobalBytes; //!< Number of bytes to allocate for global variables +} +IMGHEAD; + +/*! \page imagelayout + * + *- OBJHEAD (aligned) + * - OffsetToInstructions (4 bytes) + * - OwnerObjectId (2 bytes) + * - TriggerCount (2 bytes) + * - LocalBytes (4 bytes) + * + */ + +/*! \struct OBJHEAD + * Object header used for all types of objects (VMTHREAD, SUBCALL, BLOCK and ALIAS) + */ +typedef struct // Object header +{ + IP OffsetToInstructions; //!< Offset to instructions from image start + OBJID OwnerObjectId; //!< Used by BLOCK's to hold the owner id + TRIGGER TriggerCount; //!< Used to determine how many triggers needed before the BLOCK object is activated + LBINDEX LocalBytes; //!< Number of bytes to allocate for local variables +} +OBJHEAD; + + +/*! \struct LABEL + * Label data hold information used for labels + */ +typedef struct +{ + IMINDEX Addr; //!< Offset to breakpoint address from image start +} +LABEL; + + +#endif /* LMSTYPES_H_ */ diff --git a/brick/kernel/source/validate.h b/brick/kernel/source/validate.h new file mode 100644 index 00000000..17d118e8 --- /dev/null +++ b/brick/kernel/source/validate.h @@ -0,0 +1,56 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef VALIDATE_H_ +#define VALIDATE_H_ + +RESULT cValidateInit(void); + +RESULT cValidateExit(void); + +RESULT cValidateDisassemble(IP pI,IMINDEX *pIndex,LABEL *pLabel); + +RESULT cValidateProgram(PRGID PrgId,IP pI,LABEL *pLabel,DATA8 Disassemble); + + +typedef struct +{ + //***************************************************************************** + // Validate Global variables + //***************************************************************************** + + int Row; + IMINDEX ValidateErrorIndex; +} +VALIDATE_GLOBALS; + +#ifndef LEGO_SIMULATION +extern VALIDATE_GLOBALS ValidateInstance; +#else +extern VALIDATE_GLOBALS * gValidateInstance; +#define ValidateInstance (*gValidateInstance) + +void setValidateInstance(VALIDATE_GLOBALS * _Instance); +VALIDATE_GLOBALS * getValidateInstance(); +#endif + + + #endif /* VALIDATE_H_ */ diff --git a/brick/kernel/storage_common.c b/brick/kernel/storage_common.c new file mode 100644 index 00000000..9dfd1792 --- /dev/null +++ b/brick/kernel/storage_common.c @@ -0,0 +1,780 @@ +/* + * storage_common.c -- Common definitions for mass storage functionality + * + * Copyright (C) 2003-2008 Alan Stern + * Copyeight (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (m.nazarewicz@samsung.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * This file requires the following identifiers used in USB strings to + * be defined (each of type pointer to char): + * - fsg_string_manufacturer -- name of the manufacturer + * - fsg_string_product -- name of the product + * - fsg_string_serial -- product's serial + * - fsg_string_config -- name of the configuration + * - fsg_string_interface -- name of the interface + * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS + * macro is defined prior to including this file. + */ + +/* + * When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and + * fsg_hs_intr_in_desc objects as well as + * FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES + * macros are not defined. + * + * When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER, + * FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not + * defined (as well as corresponding entries in string tables are + * missing) and FSG_STRING_INTERFACE has value of zero. + * + * When FSG_NO_OTG is defined fsg_otg_desc won't be defined. + */ + +/* + * When FSG_BUFFHD_STATIC_BUFFER is defined when this file is included + * the fsg_buffhd structure's buf field will be an array of FSG_BUFLEN + * characters rather then a pointer to void. + */ + + +#include + + +/* Thanks to NetChip Technologies for donating this product ID. + * + * DO NOT REUSE THESE IDs with any other driver!! Ever!! + * Instead: allocate your own, using normal USB-IF procedures. */ +#define FSG_VENDOR_ID 0x0525 /* NetChip */ +#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ + + +/*-------------------------------------------------------------------------*/ + + +#ifndef DEBUG +#undef VERBOSE_DEBUG +#undef DUMP_MSGS +#endif /* !DEBUG */ + +#ifdef VERBOSE_DEBUG +#define VLDBG LDBG +#else +#define VLDBG(lun, fmt, args...) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args) +#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args) +#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args) +#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args) + +/* Keep those macros in sync with thos in + * include/linux/ubs/composite.h or else GCC will complain. If they + * are identical (the same names of arguments, white spaces in the + * same places) GCC will allow redefinition otherwise (even if some + * white space is removed or added) warning will be issued. No + * checking if those symbols is defined is performed because warning + * is desired when those macros were defined by someone else to mean + * something else. */ +#define DBG(d, fmt, args...) dev_dbg(&(d)->gadget->dev , fmt , ## args) +#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev , fmt , ## args) +#define ERROR(d, fmt, args...) dev_err(&(d)->gadget->dev , fmt , ## args) +#define WARNING(d, fmt, args...) dev_warn(&(d)->gadget->dev , fmt , ## args) +#define INFO(d, fmt, args...) dev_info(&(d)->gadget->dev , fmt , ## args) + + + +#ifdef DUMP_MSGS + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) do { \ + if (length < 512) { \ + DBG(fsg, "%s, length %u:\n", label, length); \ + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \ + 16, 1, buf, length, 0); \ + } \ +} while (0) + +# define dump_cdb(fsg) do { } while (0) + +#else + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + +# ifdef VERBOSE_DEBUG + +# define dump_cdb(fsg) \ + print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \ + 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ + +# else + +# define dump_cdb(fsg) do { } while (0) + +# endif /* VERBOSE_DEBUG */ + +#endif /* DUMP_MSGS */ + + + + + +/*-------------------------------------------------------------------------*/ + +/* SCSI device types */ +#define TYPE_DISK 0x00 +#define TYPE_CDROM 0x05 + +/* USB protocol value = the transport method */ +#define USB_PR_CBI 0x00 /* Control/Bulk/Interrupt */ +#define USB_PR_CB 0x01 /* Control/Bulk w/o interrupt */ +#define USB_PR_BULK 0x50 /* Bulk-only */ + +/* USB subclass value = the protocol encapsulation */ +#define USB_SC_RBC 0x01 /* Reduced Block Commands (flash) */ +#define USB_SC_8020 0x02 /* SFF-8020i, MMC-2, ATAPI (CD-ROM) */ +#define USB_SC_QIC 0x03 /* QIC-157 (tape) */ +#define USB_SC_UFI 0x04 /* UFI (floppy) */ +#define USB_SC_8070 0x05 /* SFF-8070i (removable) */ +#define USB_SC_SCSI 0x06 /* Transparent SCSI */ + +/* Bulk-only data structures */ + +/* Command Block Wrapper */ +struct fsg_bulk_cb_wrap { + __le32 Signature; /* Contains 'USBC' */ + u32 Tag; /* Unique per command id */ + __le32 DataTransferLength; /* Size of the data */ + u8 Flags; /* Direction in bit 7 */ + u8 Lun; /* LUN (normally 0) */ + u8 Length; /* Of the CDB, <= MAX_COMMAND_SIZE */ + u8 CDB[16]; /* Command Data Block */ +}; + +#define USB_BULK_CB_WRAP_LEN 31 +#define USB_BULK_CB_SIG 0x43425355 /* Spells out USBC */ +#define USB_BULK_IN_FLAG 0x80 + +/* Command Status Wrapper */ +struct bulk_cs_wrap { + __le32 Signature; /* Should = 'USBS' */ + u32 Tag; /* Same as original command */ + __le32 Residue; /* Amount not transferred */ + u8 Status; /* See below */ +}; + +#define USB_BULK_CS_WRAP_LEN 13 +#define USB_BULK_CS_SIG 0x53425355 /* Spells out 'USBS' */ +#define USB_STATUS_PASS 0 +#define USB_STATUS_FAIL 1 +#define USB_STATUS_PHASE_ERROR 2 + +/* Bulk-only class specific requests */ +#define USB_BULK_RESET_REQUEST 0xff +#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe + + +/* CBI Interrupt data structure */ +struct interrupt_data { + u8 bType; + u8 bValue; +}; + +#define CBI_INTERRUPT_DATA_LEN 2 + +/* CBI Accept Device-Specific Command request */ +#define USB_CBI_ADSC_REQUEST 0x00 + + +/* Length of a SCSI Command Data Block */ +#define MAX_COMMAND_SIZE 16 + +/* SCSI commands that we recognize */ +#define SC_FORMAT_UNIT 0x04 +#define SC_INQUIRY 0x12 +#define SC_MODE_SELECT_6 0x15 +#define SC_MODE_SELECT_10 0x55 +#define SC_MODE_SENSE_6 0x1a +#define SC_MODE_SENSE_10 0x5a +#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e +#define SC_READ_6 0x08 +#define SC_READ_10 0x28 +#define SC_READ_12 0xa8 +#define SC_READ_CAPACITY 0x25 +#define SC_READ_FORMAT_CAPACITIES 0x23 +#define SC_READ_HEADER 0x44 +#define SC_READ_TOC 0x43 +#define SC_RELEASE 0x17 +#define SC_REQUEST_SENSE 0x03 +#define SC_RESERVE 0x16 +#define SC_SEND_DIAGNOSTIC 0x1d +#define SC_START_STOP_UNIT 0x1b +#define SC_SYNCHRONIZE_CACHE 0x35 +#define SC_TEST_UNIT_READY 0x00 +#define SC_VERIFY 0x2f +#define SC_WRITE_6 0x0a +#define SC_WRITE_10 0x2a +#define SC_WRITE_12 0xaa + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE 0 +#define SS_COMMUNICATION_FAILURE 0x040800 +#define SS_INVALID_COMMAND 0x052000 +#define SS_INVALID_FIELD_IN_CDB 0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 +#define SS_MEDIUM_NOT_PRESENT 0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 +#define SS_RESET_OCCURRED 0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 +#define SS_UNRECOVERED_READ_ERROR 0x031100 +#define SS_WRITE_ERROR 0x030c02 +#define SS_WRITE_PROTECTED 0x072700 + +#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ +#define ASC(x) ((u8) ((x) >> 8)) +#define ASCQ(x) ((u8) (x)) + + +/*-------------------------------------------------------------------------*/ + + +struct fsg_lun { + struct file *filp; + loff_t file_length; + loff_t num_sectors; + + unsigned int initially_ro:1; + unsigned int ro:1; + unsigned int removable:1; + unsigned int cdrom:1; + unsigned int prevent_medium_removal:1; + unsigned int registered:1; + unsigned int info_valid:1; + + u32 sense_data; + u32 sense_data_info; + u32 unit_attention_data; + + struct device dev; +}; + +#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL) + +static struct fsg_lun *fsg_lun_from_dev(struct device *dev) +{ + return container_of(dev, struct fsg_lun, dev); +} + + +/* Big enough to hold our biggest descriptor */ +#define EP0_BUFSIZE 256 +#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */ + +/* Number of buffers we will use. 2 is enough for double-buffering */ +#define FSG_NUM_BUFFERS 2 + +/* Default size of buffer length. */ +#define FSG_BUFLEN ((u32)16384) + +/* Maximal number of LUNs supported in mass storage function */ +#define FSG_MAX_LUNS 8 + +enum fsg_buffer_state { + BUF_STATE_EMPTY = 0, + BUF_STATE_FULL, + BUF_STATE_BUSY +}; + +struct fsg_buffhd { +#ifdef FSG_BUFFHD_STATIC_BUFFER + char buf[FSG_BUFLEN]; +#else + void *buf; +#endif + enum fsg_buffer_state state; + struct fsg_buffhd *next; + + /* The NetChip 2280 is faster, and handles some protocol faults + * better, if we don't submit any short bulk-out read requests. + * So we will record the intended request length here. */ + unsigned int bulk_out_intended_length; + + struct usb_request *inreq; + int inreq_busy; + struct usb_request *outreq; + int outreq_busy; +}; + +enum fsg_state { + /* This one isn't used anywhere */ + FSG_STATE_COMMAND_PHASE = -10, + FSG_STATE_DATA_PHASE, + FSG_STATE_STATUS_PHASE, + + FSG_STATE_IDLE = 0, + FSG_STATE_ABORT_BULK_OUT, + FSG_STATE_RESET, + FSG_STATE_INTERFACE_CHANGE, + FSG_STATE_CONFIG_CHANGE, + FSG_STATE_DISCONNECT, + FSG_STATE_EXIT, + FSG_STATE_TERMINATED +}; + +enum data_direction { + DATA_DIR_UNKNOWN = 0, + DATA_DIR_FROM_HOST, + DATA_DIR_TO_HOST, + DATA_DIR_NONE +}; + + +/*-------------------------------------------------------------------------*/ + + +static inline u32 get_unaligned_be24(u8 *buf) +{ + return 0xffffff & (u32) get_unaligned_be32(buf - 1); +} + + +/*-------------------------------------------------------------------------*/ + + +enum { +#ifndef FSG_NO_DEVICE_STRINGS + FSG_STRING_MANUFACTURER = 1, + FSG_STRING_PRODUCT, + FSG_STRING_SERIAL, + FSG_STRING_CONFIG, +#endif + FSG_STRING_INTERFACE +}; + + +#ifndef FSG_NO_OTG +static struct usb_otg_descriptor +fsg_otg_desc = { + .bLength = sizeof fsg_otg_desc, + .bDescriptorType = USB_DT_OTG, + + .bmAttributes = USB_OTG_SRP, +}; +#endif + +/* There is only one interface. */ + +static struct usb_interface_descriptor +fsg_intf_desc = { + .bLength = sizeof fsg_intf_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, /* Adjusted during fsg_bind() */ + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */ + .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */ + .iInterface = FSG_STRING_INTERFACE, +}; + +/* Three full-speed endpoint descriptors: bulk-in, bulk-out, + * and interrupt-in. */ + +static struct usb_endpoint_descriptor +fsg_fs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +static struct usb_endpoint_descriptor +fsg_fs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_fs_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 32, /* frames -> 32 ms */ +}; + +#ifndef FSG_NO_OTG +# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 2 +#else +# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 1 +#endif + +#endif + +static struct usb_descriptor_header *fsg_fs_function[] = { +#ifndef FSG_NO_OTG + (struct usb_descriptor_header *) &fsg_otg_desc, +#endif + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, +#ifndef FSG_NO_INTR_EP + (struct usb_descriptor_header *) &fsg_fs_intr_in_desc, +#endif + NULL, +}; + + +/* + * USB 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + * + * That means alternate endpoint descriptors (bigger packets) + * and a "device qualifier" ... plus more construction options + * for the config descriptor. + */ +static struct usb_endpoint_descriptor +fsg_hs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +static struct usb_endpoint_descriptor +fsg_hs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 1, /* NAK every 1 uframe */ +}; + +#ifndef FSG_NO_INTR_EP + +static struct usb_endpoint_descriptor +fsg_hs_intr_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(2), + .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */ +}; + +#ifndef FSG_NO_OTG +# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 2 +#else +# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 1 +#endif + +#endif + +static struct usb_descriptor_header *fsg_hs_function[] = { +#ifndef FSG_NO_OTG + (struct usb_descriptor_header *) &fsg_otg_desc, +#endif + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, +#ifndef FSG_NO_INTR_EP + (struct usb_descriptor_header *) &fsg_hs_intr_in_desc, +#endif + NULL, +}; + +/* Maxpacket and other transfer characteristics vary by speed. */ +static struct usb_endpoint_descriptor * +fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs, + struct usb_endpoint_descriptor *hs) +{ + if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH) + return hs; + return fs; +} + + +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ +static struct usb_string fsg_strings[] = { +#ifndef FSG_NO_DEVICE_STRINGS + {FSG_STRING_MANUFACTURER, fsg_string_manufacturer}, + {FSG_STRING_PRODUCT, fsg_string_product}, + {FSG_STRING_SERIAL, fsg_string_serial}, + {FSG_STRING_CONFIG, fsg_string_config}, +#endif + {FSG_STRING_INTERFACE, fsg_string_interface}, + {} +}; + +static struct usb_gadget_strings fsg_stringtab = { + .language = 0x0409, /* en-us */ + .strings = fsg_strings, +}; + + + /*-------------------------------------------------------------------------*/ + +/* If the next two routines are called while the gadget is registered, + * the caller must own fsg->filesem for writing. */ + +static int fsg_lun_open(struct fsg_lun *curlun, const char *filename) +{ + int ro; + struct file *filp = NULL; + int rc = -EINVAL; + struct inode *inode = NULL; + loff_t size; + loff_t num_sectors; + loff_t min_sectors; + + /* R/W if we can, R/O if we must */ + ro = curlun->initially_ro; + if (!ro) { + filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); + if (-EROFS == PTR_ERR(filp)) + ro = 1; + } + if (ro) + filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0); + if (IS_ERR(filp)) { + LINFO(curlun, "unable to open backing file: %s\n", filename); + return PTR_ERR(filp); + } + + if (!(filp->f_mode & FMODE_WRITE)) + ro = 1; + + if (filp->f_path.dentry) + inode = filp->f_path.dentry->d_inode; + if (inode && S_ISBLK(inode->i_mode)) { + if (bdev_read_only(inode->i_bdev)) + ro = 1; + } else if (!inode || !S_ISREG(inode->i_mode)) { + LINFO(curlun, "invalid file type: %s\n", filename); + goto out; + } + + /* If we can't read the file, it's no good. + * If we can't write the file, use it read-only. */ + if (!filp->f_op || !(filp->f_op->read || filp->f_op->aio_read)) { + LINFO(curlun, "file not readable: %s\n", filename); + goto out; + } + if (!(filp->f_op->write || filp->f_op->aio_write)) + ro = 1; + + size = i_size_read(inode->i_mapping->host); + if (size < 0) { + LINFO(curlun, "unable to find file size: %s\n", filename); + rc = (int) size; + goto out; + } + num_sectors = size >> 9; /* File size in 512-byte blocks */ + min_sectors = 1; + if (curlun->cdrom) { + num_sectors &= ~3; /* Reduce to a multiple of 2048 */ + min_sectors = 300*4; /* Smallest track is 300 frames */ + if (num_sectors >= 256*60*75*4) { + num_sectors = (256*60*75 - 1) * 4; + LINFO(curlun, "file too big: %s\n", filename); + LINFO(curlun, "using only first %d blocks\n", + (int) num_sectors); + } + } + if (num_sectors < min_sectors) { + LINFO(curlun, "file too small: %s\n", filename); + rc = -ETOOSMALL; + goto out; + } + + get_file(filp); + curlun->ro = ro; + curlun->filp = filp; + curlun->file_length = size; + curlun->num_sectors = num_sectors; + LDBG(curlun, "open backing file: %s\n", filename); + rc = 0; + +out: + filp_close(filp, current->files); + return rc; +} + + +static void fsg_lun_close(struct fsg_lun *curlun) +{ + if (curlun->filp) { + LDBG(curlun, "close backing file\n"); + fput(curlun->filp); + curlun->filp = NULL; + } +} + + +/*-------------------------------------------------------------------------*/ + +/* Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). */ +static int fsg_lun_fsync_sub(struct fsg_lun *curlun) +{ + struct file *filp = curlun->filp; + + if (curlun->ro || !filp) + return 0; + return vfs_fsync(filp, filp->f_path.dentry, 1); +} + +static void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ + if (msf) { + /* Convert to Minutes-Seconds-Frames */ + addr >>= 2; /* Convert to 2048-byte frames */ + addr += 2*75; /* Lead-in occupies 2 seconds */ + dest[3] = addr % 75; /* Frames */ + addr /= 75; + dest[2] = addr % 60; /* Seconds */ + addr /= 60; + dest[1] = addr; /* Minutes */ + dest[0] = 0; /* Reserved */ + } else { + /* Absolute sector */ + put_unaligned_be32(addr, dest); + } +} + + +/*-------------------------------------------------------------------------*/ + + +static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) + ? curlun->ro + : curlun->initially_ro); +} + +static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + char *p; + ssize_t rc; + + down_read(filesem); + if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */ + p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1); + if (IS_ERR(p)) + rc = PTR_ERR(p); + else { + rc = strlen(p); + memmove(buf, p, rc); + buf[rc] = '\n'; /* Add a newline */ + buf[++rc] = 0; + } + } else { /* No file, return 0 bytes */ + *buf = 0; + rc = 0; + } + up_read(filesem); + return rc; +} + + +static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t rc = count; + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + int i; + + if (sscanf(buf, "%d", &i) != 1) + return -EINVAL; + + /* Allow the write-enable status to change only while the backing file + * is closed. */ + down_read(filesem); + if (fsg_lun_is_open(curlun)) { + LDBG(curlun, "read-only status change prevented\n"); + rc = -EBUSY; + } else { + curlun->ro = !!i; + curlun->initially_ro = !!i; + LDBG(curlun, "read-only status set to %d\n", curlun->ro); + } + up_read(filesem); + return rc; +} + +static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + int rc = 0; + + #if 0 + if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { + LDBG(curlun, "eject attempt prevented\n"); + return -EBUSY; /* "Door is locked" */ + } + #endif + + /* Remove a trailing newline */ + if (count > 0 && buf[count-1] == '\n') + ((char *) buf)[count-1] = 0; /* Ugh! */ + + /* Eject current medium */ + down_write(filesem); + if (fsg_lun_is_open(curlun)) { + fsg_lun_close(curlun); + curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; + } + + /* Load new medium */ + if (count > 0 && buf[0]) { + rc = fsg_lun_open(curlun, buf); + if (rc == 0) + curlun->unit_attention_data = + SS_NOT_READY_TO_READY_TRANSITION; + } + up_write(filesem); + return (rc < 0 ? rc : count); +} diff --git a/brick/kernel/usb_function.c b/brick/kernel/usb_function.c new file mode 100644 index 00000000..8bb2b6b8 --- /dev/null +++ b/brick/kernel/usb_function.c @@ -0,0 +1,739 @@ +/* + * LEGO® MINDSTORMS EV3 + * + * Copyright (C) 2010-2013 The LEGO Group + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * This UsbFunction file is based on and inheritated from + * the original file (f_sourcesink.c) and work done by + * David Brownell + * + * >> f_sourcesink.c - USB peripheral source/sink configuration driver << + * + * >> Copyright (C) 2003-2008 David Brownell << + * >> Copyright (C) 2008 by Nokia Corporation << + * + */ + +/* + * + * HID IN/OUT Interrupt transfer FUNCTION ... + * + */ + +struct f_rudolf { + struct usb_function function; + + struct usb_ep *in_ep; + struct usb_ep *out_ep; +}; + +enum // Used for signaling the IN stuff USB-state +{ // Data from the Brick to the HOST + USB_DATA_IDLE, // + USB_DATA_BUSY, // Ongoing USB request + USB_DATA_PENDING, // Data ready for X-fer, but USB busy + USB_DATA_READY, // Initial setting +}; + +int input_state = USB_DATA_IDLE; +struct usb_ep *save_in_ep; +struct usb_request *save_in_req; + +#ifndef PCASM +static inline struct f_rudolf *func_to_rudolf(struct usb_function *f) +{ + return container_of(f, struct f_rudolf, function); +} +#else +// Keep Eclipse happy +#endif + +static struct usb_interface_descriptor rudolf_intf = { + .bLength = sizeof rudolf_intf, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, // Just plain in and out + .bInterfaceClass = USB_CLASS_HID, // We go for NONE custom-driver + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +static struct hid_descriptor hs_hid_rudolf_desc = { + .bLength = sizeof hs_hid_rudolf_desc, + .bDescriptorType = HID_DT_HID, + .bcdHID = cpu_to_le16(0x0110), + .bCountryCode = 0x00, + .bNumDescriptors = 0x01, // "The one and only" + .desc[0].bDescriptorType = 0x22, // Report Descriptor Type - 0x22 = HID + .desc[0].wDescriptorLength = sizeof hs_hid_report_descriptor, + /*.desc[0].bDescriptorType = DYNAMIC */ + /*.desc[0].wDescriptorLenght= DYNAMIC */ +}; + +static struct hid_descriptor fs_hid_rudolf_desc = { + .bLength = sizeof fs_hid_rudolf_desc, + .bDescriptorType = HID_DT_HID, + .bcdHID = cpu_to_le16(0x0110), + .bCountryCode = 0x00, + .bNumDescriptors = 0x01, // "The one and only" + .desc[0].bDescriptorType = 0x22, // Report Descriptor Type - 0x22 = HID + .desc[0].wDescriptorLength = sizeof fs_hid_report_descriptor, + +}; + +/* full speed support: */ + +static struct usb_endpoint_descriptor rudolf_out_fs_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 1, /* 1 = 1 mSec POLL rate for FS */ +}; + +static struct usb_endpoint_descriptor rudolf_in_fs_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(64), + .bInterval = 1, /* 1 = 1 mSec POLL rate for FS */ +}; + +/* high speed support: */ + +static struct usb_endpoint_descriptor rudolf_in_hs_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = 4, /* Calculated as : + * 2^(value-1) * 125uS + * i.e. value 1: 2^(1-1) * 125 uSec = 125 uSec + * - 4: 2^(4-1) * 125 uSec = 1 mSec + */ +}; + +static struct usb_endpoint_descriptor rudolf_out_hs_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16(1024), + .bInterval = 4, /* Calculated as : + * 2^(value-1) * 125uS + * i.e. value 1: 2^(1-1) * 125 uSec = 125 uSec + * - 4: 2^(4-1) * 125 uSec = 1 mSec + */ +}; + +static struct usb_descriptor_header *hs_rudolf_descs[] = { + (struct usb_descriptor_header *) &rudolf_intf, + (struct usb_descriptor_header *) &hs_hid_rudolf_desc, + (struct usb_descriptor_header *) &rudolf_in_hs_desc, + (struct usb_descriptor_header *) &rudolf_out_hs_desc, + NULL, +}; + +static struct usb_descriptor_header *fs_rudolf_descs[] = { + (struct usb_descriptor_header *) &rudolf_intf, + (struct usb_descriptor_header *) &fs_hid_rudolf_desc, + (struct usb_descriptor_header *) &rudolf_in_fs_desc, + (struct usb_descriptor_header *) &rudolf_out_fs_desc, + NULL, +}; + +/* function-specific strings: */ + +static struct usb_string strings_rudolf[] = { + [0].s = "Xfer data to and from EV3 brick", + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_rudolf = { + .language = 0x0409, /* en-us */ + .strings = strings_rudolf, +}; + +static struct usb_gadget_strings *rudolf_strings[] = { + &stringtab_rudolf, + NULL, +}; + +/*-------------------------------------------------------------------------*/ + +static int +f_rudolf_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct f_rudolf *rudolf = func_to_rudolf(f); + int id; + + /* allocate interface ID(s) */ + id = usb_interface_id(c, f); + if (id < 0) + return id; + rudolf_intf.bInterfaceNumber = id; + + /* allocate endpoints */ + rudolf->in_ep = usb_ep_autoconfig(cdev->gadget, &rudolf_in_fs_desc); + if (!rudolf->in_ep) { +autoconf_fail: + ERROR(cdev, "%s: can't autoconfigure on %s\n", + f->name, cdev->gadget->name); + return -ENODEV; + } + rudolf->in_ep->driver_data = cdev; /* claim */ + + rudolf->out_ep = usb_ep_autoconfig(cdev->gadget, &rudolf_out_fs_desc); + if (!rudolf->out_ep) + goto autoconf_fail; + rudolf->out_ep->driver_data = cdev; /* claim */ + + + /* support high speed hardware */ + if (gadget_is_dualspeed(c->cdev->gadget)) { + + rudolf_in_hs_desc.bEndpointAddress = + rudolf_in_fs_desc.bEndpointAddress; + rudolf_out_hs_desc.bEndpointAddress = + rudolf_out_fs_desc.bEndpointAddress; + f->hs_descriptors = hs_rudolf_descs; + f->descriptors = fs_rudolf_descs; + } + + DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", + gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", + f->name, rudolf->in_ep->name, rudolf->out_ep->name); + return 0; +} + +static void +f_rudolf_unbind(struct usb_configuration *c, struct usb_function *f) +{ + kfree(func_to_rudolf(f)); +} + +static void usb_req_arm(struct usb_ep *ep, struct usb_request *req) +{ + int status; + + if (UsbSpeed.Speed == FULL_SPEED) + { + req->length = 64; + req->actual = 64; + } + else + { + req->length = 1024; + req->actual = 1024; + } + + status = usb_ep_queue(ep, req, GFP_ATOMIC); + if (status) { + usb_ep_set_halt(ep); + /* FIXME recover later ... somehow */ + } +} + +static int read_data_from_host(struct usb_request *req) +{ + unsigned i; + u8 *buf = req->buf; + + int from_host_length = 0; // NO ACCESS LOCKS YET + + // test for actual length > 0 + + for (i = 0; i < req->actual; i++, buf++) + { + + usb_char_buffer_out[i] = *buf; + from_host_length++; + } + + return (from_host_length); +} + +static void write_data_to_the_host(struct usb_ep *ep, struct usb_request *req) +{ + unsigned i; + u8 *buf = req->buf; + + //#define DEBUG + #ifdef DEBUG + printk("WR to HOST req->length = %d\r\n", req->length); + #endif + + #undef DEBUG + //#define DEBUG + #ifdef DEBUG + printk("USB = %d, %d\r\n", usb_char_buffer_in[2], usb_char_buffer_in[3]); + #endif + #undef DEBUG + + for (i = 0; i < req->length; i++) + *buf++ = usb_char_buffer_in[i]; + usb_char_in_length = 0; // Reset and ready +} + +static void rudolf_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_rudolf *rudolf = ep->driver_data; + int status = req->status; + + + switch ( status ) { + + case 0: /* normal completion? */ + if (ep == rudolf->out_ep) // An OUT completion? + { + //#define DEBUG + #ifdef DEBUG + printk("Rudolf_complete OUT\n"); + #endif + + usb_char_out_length = read_data_from_host(req); + usb_req_arm(ep, req); + } + else // We have an INPUT request complete + { + //#define DEBUG + #ifdef DEBUG + printk("Rudolf_complete IN\n"); + #endif + + switch(input_state) // State of Brick data x-fer + { + case USB_DATA_READY: //should be BUSY or PENDING.... + + #ifdef DEBUG + printk("IN_IN_IN - READY ?????\n"); + #endif + + break; + + case USB_DATA_PENDING: // + // #define DEBUG + #ifdef DEBUG + printk("IN_IN_IN - PENDING settes to BUSY\n"); + #endif + + input_state = USB_DATA_BUSY; + write_data_to_the_host(ep, req); + usb_req_arm(ep, req); // new request + break; + + case USB_DATA_BUSY: // + #ifdef DEBUG + printk("IN_IN_IN - BUSY settes to READY\n"); + #endif + #undef DEBUG + input_state = USB_DATA_READY; + // and relax + break; + + case USB_DATA_IDLE: // too lazy + #ifdef DEBUG + printk("IN_IN_IN - IDLE\n"); + #endif + //#undef DEBUG + + break; + + default: break; // hmmm. + } + // Reset the buffer size - Ready again + usb_char_in_length = 0; + } + break; + + /* this endpoint is normally active while we're configured */ + + case -ESHUTDOWN: /* disconnect from host */ + // REMOVED 26102012 (*pUsbSpeed).Speed = FULL_SPEED; + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + //case -ESHUTDOWN: /* disconnect from host */ + if (ep == rudolf->out_ep) + read_data_from_host(req); + free_ep_req(ep, req); + return; + + case -EOVERFLOW: /* buffer overrun on read means that + we didn't provide a big enough + buffer. + */ + default: +//#if 1 + // DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name, + // status, req->actual, req->length); +//#endif + case -EREMOTEIO: /* short read */ + break; + } + +} + +static int rudolf_start_ep(struct f_rudolf *rudolf, bool is_in) +{ + struct usb_ep *ep; + struct usb_request *req; + int status; + + ep = is_in ? rudolf->in_ep : rudolf->out_ep; + req = alloc_ep_req(ep); + if (!req) + return -ENOMEM; + + req->complete = rudolf_complete; + + #ifdef DEBUG + printk("UsbSpeed.Speed = %d\n\r", UsbSpeed.Speed); + #endif + + if (UsbSpeed.Speed == FULL_SPEED) + { + #ifdef DEBUG + printk("rudolf_start_ep FULL\n\r"); + #endif + + (*pUsbSpeed).Speed = FULL_SPEED; + req->length = 64; // Full speed max buffer size + req->actual = 64; + } + else + { + #ifdef DEBUG + printk("rudolf_start_ep HIGH\n\r"); + #endif + + (*pUsbSpeed).Speed = HIGH_SPEED; + req->length = 1024; // High speed max buffer size + req->actual = 1024; + } + + if (is_in) + { + save_in_ep = ep; + save_in_req = req; + + #ifdef DEBUG + printk("req->length = %d ***** Rudolf_Start_Ep_in\n\r", req->length); + #endif + + // reinit_write_data(ep, req); + input_state = USB_DATA_BUSY; + } + else + { + #ifdef DEBUG + printk("***** Rudolf_Start_Ep_out\n"); + #endif + } + + status = usb_ep_queue(ep, req, GFP_ATOMIC); + + if (status) { + struct usb_composite_dev *cdev; + + cdev = rudolf->function.config->cdev; + ERROR(cdev, "start %s %s --> %d\n", + is_in ? "IN" : "OUT", + ep->name, status); + + free_ep_req(ep, req); + } + + return status; +} + +static void disable_rudolf(struct f_rudolf *rudolf) +{ + struct usb_composite_dev *cdev; + + cdev = rudolf->function.config->cdev; + disable_endpoints(cdev, rudolf->in_ep, rudolf->out_ep); + VDBG(cdev, "%s disabled\n", rudolf->function.name); +} + +static int +enable_rudolf(struct usb_composite_dev *cdev, struct f_rudolf *rudolf) +{ + int result = 0; + const struct usb_endpoint_descriptor *ep_in, *ep_out; + struct usb_ep *ep; + + ep_in = ep_choose(cdev->gadget, &rudolf_in_hs_desc, &rudolf_in_fs_desc); + + ep_out = ep_choose(cdev->gadget, &rudolf_out_hs_desc, &rudolf_out_fs_desc); + + ep = rudolf->in_ep; + + result = usb_ep_enable(ep, ep_in); + + if (result < 0) + return result; + + ep->driver_data = rudolf; + + result = rudolf_start_ep(rudolf, true); + + if (result < 0) { +fail: + ep = rudolf->in_ep; + usb_ep_disable(ep); + ep->driver_data = NULL; + return result; + } + + /* one endpoint reads (sinks) anything OUT (from the host) */ + ep = rudolf->out_ep; + result = usb_ep_enable(ep, ep_out); + + if (result < 0) + goto fail; + + ep->driver_data = rudolf; + + result = rudolf_start_ep(rudolf, false); + + if (result < 0) { + usb_ep_disable(ep); + ep->driver_data = NULL; + goto fail; + } + + DBG(cdev, "%s enabled\n", rudolf->function.name); + + return result; +} + +static int f_rudolf_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct f_rudolf *rudolf = func_to_rudolf(f); + struct usb_composite_dev *cdev = f->config->cdev; + + /* we know alt is zero */ + if (rudolf->in_ep->driver_data) + disable_rudolf(rudolf); + + return enable_rudolf(cdev, rudolf); +} + +static void f_rudolf_disable(struct usb_function *f) +{ + struct f_rudolf *rudolf = func_to_rudolf(f); + + disable_rudolf(rudolf); +} + +/*-------------------------------------------------------------------------*/ + +static int msg_config(struct usb_configuration *c); + +static int rudolf_bind_config(struct usb_configuration *c) +{ + struct f_rudolf *rudolf; + int status; + + rudolf = kzalloc(sizeof *rudolf, GFP_KERNEL); + if (!rudolf) + return -ENOMEM; + + rudolf->function.name = "rudolf xfer"; + rudolf->function.descriptors = hs_rudolf_descs; + rudolf->function.bind = f_rudolf_bind; + rudolf->function.unbind = f_rudolf_unbind; + rudolf->function.set_alt = f_rudolf_set_alt; + rudolf->function.disable = f_rudolf_disable; + + status = usb_add_function(c, &rudolf->function); + if (status) { + kfree(rudolf); + return status; + } + + + status = msg_config(c); + if (status) { + kfree(rudolf); + return status; + } + + return status; +} + +#ifndef PCASM +static int rudolf_setup(struct usb_configuration *c, + const struct usb_ctrlrequest *ctrl) +{ + struct usb_request *req = c->cdev->req; + int value = -EOPNOTSUPP; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + u16 length = 0; + + /* composite driver infrastructure handles everything except + * the two control test requests. + */ + + switch (ctrl->bRequest) { + + /* + * These are the same vendor-specific requests supported by + * Intel's USB 2.0 compliance test devices. We exceed that + * device spec by allowing multiple-packet requests. + * + * NOTE: the Control-OUT data stays in req->buf ... better + * would be copying it into a scratch buffer, so that other + * requests may safely intervene. + */ + + case 0x5b: /* control WRITE test -- fill the buffer */ + if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) + goto unknown; + if (w_value || w_index) + break; + /* just read that many bytes into the buffer */ + if (w_length > req->length) + break; + value = w_length; + break; + case 0x5c: /* control READ test -- return the buffer */ + if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) + goto unknown; + if (w_value || w_index) + break; + /* expect those bytes are still in the buffer; send back */ + if (w_length > req->length) + break; + value = w_length; + break; + + default: +unknown: + VDBG(c->cdev, + "unknown control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + } + + //HER SKAL HID DESC SENDES!!! + switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { + + + case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8 + | USB_REQ_GET_DESCRIPTOR): + switch (w_value >> 8) { + case HID_DT_REPORT: + //VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n"); + length = w_length; + length = min_t(unsigned short, length, + sizeof hs_hid_report_descriptor); + memcpy(req->buf, hs_hid_report_descriptor, length); + value = length; + goto respond; + break; + + default: + //VDBG(cdev, "Unknown decriptor request 0x%x\n", + // value >> 8); + goto stall; + break; + } + break; + default: + //VDBG(cdev, "Unknown request 0x%x\n", + // ctrl->bRequest); + goto stall; + break; + } + +//HERTIL + /* respond with data transfer or status phase? */ +stall: + return -EOPNOTSUPP; + +respond: + if (value >= 0) { + VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + w_value, w_index, w_length); + req->zero = 0; + req->length = value; + value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) + ERROR(c->cdev, "source/sinkc response, err %d\n", + value); + } + + /* device either stalls (value < 0) or reports success */ + return value; +} +#else +// Keep Eclipse happy +#endif + +static struct usb_configuration rudolf_driver = { + .label = "rudolf driver", + .strings = rudolf_strings, + .bind = rudolf_bind_config, + .setup = rudolf_setup, + .bConfigurationValue = 1, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + /* .iConfiguration = DYNAMIC */ +}; + +/** + * + */ + +int rudolf_add(struct usb_composite_dev *cdev, bool autoresume) +{ + int id; + + /* allocate string ID(s) */ + id = usb_string_id(cdev); + if (id < 0) + return id; + strings_rudolf[0].id = id; + + rudolf_intf.iInterface = id; + rudolf_driver.iConfiguration = 1; // id; + + /* support autoresume for remote wakeup testing */ + if (autoresume) + rudolf_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + + /* support OTG systems */ + if (gadget_is_otg(cdev->gadget)) { + rudolf_driver.descriptors = otg_desc; + rudolf_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP; + } + + + return usb_add_config(cdev, &rudolf_driver); +}