linux/drivers/usb/storage/option_ms.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Driver for Option High Speed Mobile Devices.
   4 *
   5 *   (c) 2008 Dan Williams <dcbw@redhat.com>
   6 *
   7 * Inspiration taken from sierra_ms.c by Kevin Lloyd <klloyd@sierrawireless.com>
   8 */
   9
  10#include <linux/usb.h>
  11#include <linux/slab.h>
  12#include <linux/module.h>
  13
  14#include "usb.h"
  15#include "transport.h"
  16#include "option_ms.h"
  17#include "debug.h"
  18
  19#define ZCD_FORCE_MODEM                 0x01
  20#define ZCD_ALLOW_MS                    0x02
  21
  22static unsigned int option_zero_cd = ZCD_FORCE_MODEM;
  23module_param(option_zero_cd, uint, S_IRUGO | S_IWUSR);
  24MODULE_PARM_DESC(option_zero_cd, "ZeroCD mode (1=Force Modem (default),"
  25                 " 2=Allow CD-Rom");
  26
  27#define RESPONSE_LEN 1024
  28
  29static int option_rezero(struct us_data *us)
  30{
  31        static const unsigned char rezero_msg[] = {
  32          0x55, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12,
  33          0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x01,
  34          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  35          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  36        };
  37        char *buffer;
  38        int result;
  39
  40        usb_stor_dbg(us, "Option MS: %s\n", "DEVICE MODE SWITCH");
  41
  42        buffer = kzalloc(RESPONSE_LEN, GFP_KERNEL);
  43        if (buffer == NULL)
  44                return USB_STOR_TRANSPORT_ERROR;
  45
  46        memcpy(buffer, rezero_msg, sizeof(rezero_msg));
  47        result = usb_stor_bulk_transfer_buf(us,
  48                        us->send_bulk_pipe,
  49                        buffer, sizeof(rezero_msg), NULL);
  50        if (result != USB_STOR_XFER_GOOD) {
  51                result = USB_STOR_XFER_ERROR;
  52                goto out;
  53        }
  54
  55        /*
  56         * Some of the devices need to be asked for a response, but we don't
  57         * care what that response is.
  58         */
  59        usb_stor_bulk_transfer_buf(us,
  60                        us->recv_bulk_pipe,
  61                        buffer, RESPONSE_LEN, NULL);
  62
  63        /* Read the CSW */
  64        usb_stor_bulk_transfer_buf(us,
  65                        us->recv_bulk_pipe,
  66                        buffer, 13, NULL);
  67
  68        result = USB_STOR_XFER_GOOD;
  69
  70out:
  71        kfree(buffer);
  72        return result;
  73}
  74
  75static int option_inquiry(struct us_data *us)
  76{
  77        static const unsigned char inquiry_msg[] = {
  78          0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
  79          0x24, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x12,
  80          0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00,
  81          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  82        };
  83        char *buffer;
  84        int result;
  85
  86        usb_stor_dbg(us, "Option MS: %s\n", "device inquiry for vendor name");
  87
  88        buffer = kzalloc(0x24, GFP_KERNEL);
  89        if (buffer == NULL)
  90                return USB_STOR_TRANSPORT_ERROR;
  91
  92        memcpy(buffer, inquiry_msg, sizeof(inquiry_msg));
  93        result = usb_stor_bulk_transfer_buf(us,
  94                        us->send_bulk_pipe,
  95                        buffer, sizeof(inquiry_msg), NULL);
  96        if (result != USB_STOR_XFER_GOOD) {
  97                result = USB_STOR_XFER_ERROR;
  98                goto out;
  99        }
 100
 101        result = usb_stor_bulk_transfer_buf(us,
 102                        us->recv_bulk_pipe,
 103                        buffer, 0x24, NULL);
 104        if (result != USB_STOR_XFER_GOOD) {
 105                result = USB_STOR_XFER_ERROR;
 106                goto out;
 107        }
 108
 109        result = memcmp(buffer+8, "Option", 6);
 110
 111        if (result != 0)
 112                result = memcmp(buffer+8, "ZCOPTION", 8);
 113
 114        /* Read the CSW */
 115        usb_stor_bulk_transfer_buf(us,
 116                        us->recv_bulk_pipe,
 117                        buffer, 13, NULL);
 118
 119out:
 120        kfree(buffer);
 121        return result;
 122}
 123
 124
 125int option_ms_init(struct us_data *us)
 126{
 127        int result;
 128
 129        usb_stor_dbg(us, "Option MS: %s\n", "option_ms_init called");
 130
 131        /*
 132         * Additional test for vendor information via INQUIRY,
 133         * because some vendor/product IDs are ambiguous
 134         */
 135        result = option_inquiry(us);
 136        if (result != 0) {
 137                usb_stor_dbg(us, "Option MS: %s\n",
 138                             "vendor is not Option or not determinable, no action taken");
 139                return 0;
 140        } else
 141                usb_stor_dbg(us, "Option MS: %s\n",
 142                             "this is a genuine Option device, proceeding");
 143
 144        /* Force Modem mode */
 145        if (option_zero_cd == ZCD_FORCE_MODEM) {
 146                usb_stor_dbg(us, "Option MS: %s\n", "Forcing Modem Mode");
 147                result = option_rezero(us);
 148                if (result != USB_STOR_XFER_GOOD)
 149                        usb_stor_dbg(us, "Option MS: %s\n",
 150                                     "Failed to switch to modem mode");
 151                return -EIO;
 152        } else if (option_zero_cd == ZCD_ALLOW_MS) {
 153                /* Allow Mass Storage mode (keep CD-Rom) */
 154                usb_stor_dbg(us, "Option MS: %s\n",
 155                             "Allowing Mass Storage Mode if device requests it");
 156        }
 157
 158        return 0;
 159}
 160
 161