| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- /*
- * Rawbulk Gadget Function Driver from VIA Telecom
- *
- * Copyright (C) 2011 VIA Telecom, Inc.
- * Author: Juelun Guo (jlguo@via-telecom.com)
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
- #define DRIVER_AUTHOR "jlguo <jlguo@via-telecom.com>"
- #define DRIVER_DESC "Rawbulk Gadget - transport data from CP to Gadget"
- #define DRIVER_VERSION "1.0.1"
- #include <linux/err.h>
- #include <linux/wakelock.h>
- #include <linux/interrupt.h>
- #include <linux/delay.h>
- #include <linux/wait.h>
- #include <linux/version.h>
- #include <linux/types.h>
- #include <linux/device.h>
- #include <linux/usb/composite.h>
- #include "viatel_rawbulk.h"
- /* int setdtr, data_connect; */
- /* struct work_struct flow_control; */
- /* struct work_struct dtr_status; */
- #define function_to_rbf(f) container_of(f, struct rawbulk_function, function)
- static int usb_state;
- void simple_setup_complete(struct usb_ep *ep, struct usb_request *req)
- {
- ; /* DO NOTHING */
- }
- #define FINISH_DISABLE 0
- #define START_DISABLE 1
- int rawbulk_function_setup(struct usb_function *f, const struct
- usb_ctrlrequest * ctrl)
- {
- struct rawbulk_function *fn = function_to_rbf(f);
- unsigned int setdtr = 0;
- unsigned int data_connect = 0;
- struct usb_composite_dev *cdev = f->config->cdev;
- struct usb_request *req = 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);
- C2K_NOTE("%s\n", __func__);
- if (ctrl->bRequest) {
- C2K_NOTE("ctrl->bRequestType = %0x ctrl->bRequest = %0x\n", ctrl->bRequestType,
- ctrl->bRequest);
- }
- switch (ctrl->bRequest) {
- case 0x01:
- if (ctrl->bRequestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { /* 0x40 */
- /* set/clear DTR */
- C2K_NOTE("setdtr = %d, w_value =%d\n", setdtr, w_value);
- if (fn->activated) {
- setdtr = w_value & 0x01;
- /* schedule_work(&flow_control); */
- modem_dtr_set(setdtr, 0);
- modem_dcd_state();
- }
- value = 0;
- }
- break;
- case 0x02:
- if (ctrl->bRequestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { /* 0xC0 */
- /* DSR | CD109 */
- /* schedule_work(&dtr_status); */
- data_connect = modem_dcd_state();
- /* modem_dtr_query(&data_connect, 0); */
- if (fn->activated) {
- if (data_connect && fn->enable) {
- *((unsigned char *)req->buf) = 0x3;
- C2K_NOTE("connect %d\n", data_connect);
- } else {
- *((unsigned char *)req->buf) = 0x2;
- C2K_NOTE("disconnect=%d, setdtr=%d\n", data_connect,
- setdtr);
- }
- } else /* set CD CSR state to 0 if modem bypass not inactive */
- *((unsigned char *)req->buf) = 0x0;
- value = 1;
- }
- break;
- case 0x03:
- if (ctrl->bRequestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { /* 0x40 */
- /* xcvr */
- C2K_NOTE("CTRL SET XCVR 0x%02x\n", w_value);
- value = 0;
- }
- break;
- case 0x04:
- if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) {
- if (ctrl->bRequestType & USB_DIR_IN) { /* 0xC0 */
- /* return ID */
- sprintf(req->buf, "CBP_8.2");
- value = 1;
- } else { /* 0x40 */
- value = 0;
- }
- }
- break;
- case 0x05:
- if (ctrl->bRequestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { /* 0xC0 */
- /* connect status */
- C2K_NOTE("CTRL CONNECT STATUS\n");
- *((unsigned char *)req->buf) = 0x0;
- value = 1;
- }
- break;
- default:
- C2K_NOTE("invalid control req%02x.%02x v%04x i%04x l%d\n",
- ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length);
- }
- /* respond with data transfer or status phase? */
- if (value >= 0) {
- req->zero = 0;
- req->length = value;
- req->complete = simple_setup_complete;
- value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
- if (value < 0)
- C2K_ERR("response err %d\n", value);
- }
- /* device either stalls (value < 0) or reports success */
- return value;
- }
- static void rawbulk_auto_reconnect(int transfer_id)
- {
- int rc;
- struct rawbulk_function *fn = rawbulk_lookup_function(transfer_id);
- C2K_NOTE("%s\n", __func__);
- if (!fn || fn->autoreconn == 0)
- return;
- if (rawbulk_check_enable(fn) && fn->activated) {
- C2K_NOTE("start %s again automatically.\n", fn->longname);
- rc = rawbulk_start_transactions(transfer_id, fn->nups,
- fn->ndowns, fn->upsz, fn->downsz);
- if (rc < 0)
- rawbulk_disable_function(fn);
- }
- }
- int rawbulk_function_bind(struct usb_configuration *c, struct
- usb_function * f)
- {
- int rc, ifnum;
- struct rawbulk_function *fn = function_to_rbf(f);
- struct usb_gadget *gadget = c->cdev->gadget;
- struct usb_ep *ep_out, *ep_in;
- C2K_NOTE("%s\n", __func__);
- rc = usb_interface_id(c, f);
- if (rc < 0)
- return rc;
- ifnum = rc;
- fn->interface.bInterfaceNumber = cpu_to_le16(ifnum);
- ep_out = usb_ep_autoconfig(gadget, (struct usb_endpoint_descriptor *)
- fn->fs_descs[BULKOUT_DESC]);
- if (!ep_out) {
- C2K_ERR("%s %d config ep_out error\n", __func__, __LINE__);
- return -ENOMEM;
- }
- ep_in = usb_ep_autoconfig(gadget, (struct usb_endpoint_descriptor *)
- fn->fs_descs[BULKIN_DESC]);
- if (!ep_in) {
- usb_ep_disable(ep_out);
- C2K_ERR("%s %d config ep_in error\n", __func__, __LINE__);
- return -ENOMEM;
- }
- ep_out->driver_data = fn;
- ep_in->driver_data = fn;
- fn->bulk_out = ep_out;
- fn->bulk_in = ep_in;
- f->fs_descriptors = usb_copy_descriptors(fn->fs_descs);
- if (unlikely(!f->fs_descriptors))
- return -ENOMEM;
- if (gadget_is_dualspeed(gadget)) {
- fn->hs_bulkin_endpoint.bEndpointAddress = fn->fs_bulkin_endpoint.bEndpointAddress;
- fn->hs_bulkout_endpoint.bEndpointAddress = fn->fs_bulkout_endpoint.bEndpointAddress;
- }
- fn->cdev = c->cdev;
- fn->activated = 0;
- return rawbulk_bind_function(fn->transfer_id, f, ep_out, ep_in, rawbulk_auto_reconnect);
- }
- void rawbulk_function_unbind(struct usb_configuration *c, struct
- usb_function * f)
- {
- struct rawbulk_function *fn = function_to_rbf(f);
- C2K_NOTE("%s\n", __func__);
- /* cancel_work_sync(&fn->activator); */
- /* cancel_work_sync(&flow_control); */
- /* cancel_work_sync(&dtr_status); */
- rawbulk_unbind_function(fn->transfer_id);
- }
- /*void modem_flow_control(struct work_struct *work) {
- modem_dtr_set(!!setdtr);
- modem_dtr_query(&data_connect, 1);
- }
- void dtr_status_query(struct work_struct *work) {
- modem_dtr_query(&data_connect, 1);
- }
- */
- static void do_activate(struct work_struct *data)
- {
- struct rawbulk_function *fn = container_of(data, struct rawbulk_function,
- activator);
- int rc;
- struct usb_function *functionp = &(fn->function);
- C2K_NOTE("%s usb state %s, <%d>\n", __func__, (fn->activated ? "connect" : "disconnect"),
- fn->transfer_id);
- if (fn->activated) { /* enumerated */
- /* enabled endpoints */
- rc = config_ep_by_speed(fn->cdev->gadget, functionp, fn->bulk_out);
- if (rc < 0) {
- C2K_ERR("failed to config speed rawbulk %s %d\n",
- fn->bulk_out->name, rc);
- return;
- }
- rc = usb_ep_enable(fn->bulk_out);
- if (rc < 0) {
- C2K_ERR("failed to enable rawbulk %s %d\n", fn->bulk_out->name, rc);
- return;
- }
- rc = config_ep_by_speed(fn->cdev->gadget, functionp, fn->bulk_in);
- if (rc < 0) {
- C2K_ERR("failed to config speed rawbulk %s %d\n",
- fn->bulk_in->name, rc);
- return;
- }
- rc = usb_ep_enable(fn->bulk_in);
- if (rc < 0) {
- C2K_ERR("failed to enable rawbulk %s %d\n", fn->bulk_in->name, rc);
- usb_ep_disable(fn->bulk_out);
- return;
- }
- /* start rawbulk if enabled */
- if (rawbulk_check_enable(fn)) {
- wake_lock(&fn->keep_awake);
- rc = rawbulk_start_transactions(fn->transfer_id, fn->nups,
- fn->ndowns, fn->upsz, fn->downsz);
- if (rc < 0)
- /* rawbulk_disable_function(fn); */
- C2K_ERR("%s: failed to bypass, channel id = %d\n",
- __func__, fn->transfer_id);
- }
- } else { /* disconnect */
- if (rawbulk_check_enable(fn)) {
- if (fn->transfer_id == RAWBULK_TID_MODEM) {
- /* this in interrupt, but DTR need be set firstly then clear it
- * */
- modem_dtr_set(1, 1);
- modem_dtr_set(0, 1);
- modem_dtr_set(1, 1);
- modem_dcd_state();
- }
- rawbulk_stop_transactions(fn->transfer_id);
- /* keep the enable state, so we can enable again in next time */
- /* set_enable_state(fn, 0); */
- wake_unlock(&fn->keep_awake);
- }
- usb_ep_disable(fn->bulk_out);
- usb_ep_disable(fn->bulk_in);
- fn->bulk_out->driver_data = NULL;
- fn->bulk_in->driver_data = NULL;
- }
- }
- static void rawbulk_usb_state_set(int state)
- {
- usb_state = state ? 1 : 0;
- C2K_NOTE("%s usb_state = %d\n", __func__, usb_state);
- }
- int rawbulk_usb_state_check(void)
- {
- return usb_state ? 1 : 0;
- }
- EXPORT_SYMBOL_GPL(rawbulk_usb_state_check);
- static int rawbulk_function_setalt(struct usb_function *f, unsigned intf, unsigned alt)
- {
- struct rawbulk_function *fn = function_to_rbf(f);
- C2K_NOTE("%s\n", __func__);
- fn->activated = 1;
- rawbulk_usb_state_set(fn->activated);
- schedule_work(&fn->activator);
- return 0;
- }
- static void rawbulk_function_disable(struct usb_function *f)
- {
- struct rawbulk_function *fn = function_to_rbf(f);
- C2K_NOTE("%s, %s\n", __func__, f->name);
- fn->activated = 0;
- rawbulk_usb_state_set(fn->activated);
- schedule_work(&fn->activator);
- }
- int rawbulk_bind_config(struct usb_configuration *c, int transfer_id)
- {
- int rc;
- struct rawbulk_function *fn = rawbulk_lookup_function(transfer_id);
- if (!fn)
- return -ENOMEM;
- C2K_NOTE("add %s to config.\n", fn->longname);
- if (fn->string_defs[0].id == 0) {
- rc = usb_string_id(c->cdev);
- if (rc < 0)
- return rc;
- fn->string_defs[0].id = rc;
- fn->interface.iInterface = rc;
- }
- if (!fn->initialized) {
- fn->function.name = fn->longname;
- fn->function.setup = rawbulk_function_setup;
- fn->function.bind = rawbulk_function_bind;
- fn->function.unbind = rawbulk_function_unbind;
- fn->function.set_alt = rawbulk_function_setalt;
- fn->function.disable = rawbulk_function_disable;
- INIT_WORK(&fn->activator, do_activate);
- /*if(fn->transfer_id == RAWBULK_TID_MODEM) {
- INIT_WORK(&flow_control, modem_flow_control);
- INIT_WORK(&dtr_status, dtr_status_query);
- } */
- fn->initialized = 1;
- }
- rc = usb_add_function(c, &fn->function);
- if (rc < 0)
- C2K_ERR("%s - failed to config %d.\n", __func__, rc);
- return rc;
- }
|