Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/drivers/usb/umsc.c need to be fixed.

0001 /*                    The Quest Operating System
0002  *  Copyright (C) 2005-2010  Richard West, Boston University
0003  *
0004  *  This program is free software: you can redistribute it and/or modify
0005  *  it under the terms of the GNU General Public License as published by
0006  *  the Free Software Foundation, either version 3 of the License, or
0007  *  (at your option) any later version.
0008  *
0009  *  This program is distributed in the hope that it will be useful,
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *  GNU General Public License for more details.
0013  *
0014  *  You should have received a copy of the GNU General Public License
0015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 /* USB Mass Storage Class driver */
0019 #include "drivers/usb/usb.h"
0020 #include "drivers/usb/uhci.h"
0021 #include "util/printf.h"
0022 #include "sched/vcpu.h"
0023 #include "sched/sched.h"
0024 #include "kernel.h"
0025 
0026 #define USB_MASS_STORAGE_CLASS 0x8
0027 #define UMSC_PROTOCOL 0x50
0028 
0029 //#define DEBUG_UMSC
0030 
0031 #ifdef DEBUG_UMSC
0032 #define DLOG(fmt,...) DLOG_PREFIX("umsc",fmt,##__VA_ARGS__)
0033 #else
0034 #define DLOG(fmt,...) ;
0035 #endif
0036 
0037 #define UMSC_CBW_SIGNATURE 0x43425355 /* "USBC" (little-endian) */
0038 struct umsc_cbw {
0039   uint32 dCBWSignature;
0040   uint32 dCBWTag;
0041   uint32 dCBWDataTransferLength;
0042   union {
0043     uint8 raw;
0044     struct {
0045       uint8 _reserved:7;
0046       uint8 direction:1;
0047     };
0048   } bmCBWFlags;
0049   uint8 bCBWLUN:4;
0050   uint8 _reserved1:4;
0051   uint8 bCBWCBLength:5;
0052   uint8 _reserved2:3;
0053   uint8 CBWCB[16];
0054 } PACKED;
0055 typedef struct umsc_cbw UMSC_CBW;
0056 
0057 #define UMSC_CSW_SIGNATURE 0x53425355 /* "USBS" (little-endian) */
0058 struct umsc_csw {
0059   uint32 dCSWSignature;
0060   uint32 dCSWTag;
0061   uint32 dCSWDataResidue;
0062   uint8  bCSWStatus;
0063 } PACKED;
0064 typedef struct umsc_csw UMSC_CSW;
0065 
0066 static uint testaddr, testepout, testepin;
0067 
0068 typedef struct {
0069   USB_DEVICE_INFO *devinfo;
0070   uint ep_out, ep_in, maxpkt, last_lba, sector_size;
0071 } umsc_device_t;
0072 
0073 #define UMSC_MAX_DEVICES 16
0074 static umsc_device_t umsc_devs[UMSC_MAX_DEVICES];
0075 static uint num_umsc_devs=0;
0076 
0077 sint
0078 umsc_bulk_scsi (uint addr, uint ep_out, uint ep_in,
0079                 uint8 cmd[16], uint dir, uint8* data,
0080                 uint data_len, uint maxpkt)
0081 {
0082   UMSC_CBW cbw;
0083   UMSC_CSW csw;
0084   sint status;
0085   uint32 act_len;
0086 
0087   DLOG ("cmd: %.02X %.02X %.02X %.02X %.02X %.02X %.02X %.02X %.02X %.02X %.02X %.02X %.02X %.02X %.02X %.02X",
0088         cmd[0], cmd[1], cmd[2], cmd[3],
0089         cmd[4], cmd[5], cmd[6], cmd[7],
0090         cmd[8], cmd[9], cmd[10], cmd[11],
0091         cmd[12], cmd[13], cmd[14], cmd[15]);
0092 
0093   memset (&cbw, 0, sizeof (cbw));
0094   memset (&csw, 0, sizeof (csw));
0095 
0096   cbw.dCBWSignature = UMSC_CBW_SIGNATURE;
0097   cbw.dCBWDataTransferLength = data_len;
0098   cbw.bmCBWFlags.direction = dir;
0099   cbw.bCBWCBLength = 16;            /* cmd length */
0100   memcpy (cbw.CBWCB, cmd, 16);
0101 
0102   status = uhci_bulk_transfer (addr, ep_out, &cbw, 0x1f, maxpkt, DIR_OUT, &act_len);
0103 
0104   DLOG ("status=%d", status);
0105 
0106   if (data_len > 0) {
0107     if (dir) {
0108       status = uhci_bulk_transfer (addr, ep_in, data, data_len, maxpkt, DIR_IN, &act_len);
0109     }
0110     else {
0111       status = uhci_bulk_transfer (addr, ep_out, data, data_len, maxpkt, DIR_OUT, &act_len);
0112     }
0113 
0114     DLOG ("status=%d", status);
0115 
0116     if (status != 0) return status;
0117 
0118     DLOG ("data=%.02X %.02X %.02X %.02X", data[0], data[1], data[2], data[3]);
0119 
0120   }
0121 
0122   status = uhci_bulk_transfer (addr, ep_in, (addr_t) &csw, 0x0d, maxpkt, DIR_IN, &act_len);
0123 
0124   DLOG ("status=%d", status);
0125 
0126   if (status != 0) return status;
0127 
0128   DLOG ("csw sig=%p tag=%p res=%d status=%d",
0129         csw.dCSWSignature, csw.dCSWTag, csw.dCSWDataResidue, csw.bCSWStatus);
0130 
0131   return status;
0132 }
0133 
0134 sint
0135 _umsc_read_sector (uint dev_index, uint32 lba, uint8 *sector, uint len)
0136 {
0137   umsc_device_t *umsc;
0138   uint8 cmd[16] = { [0] = 0x28,
0139                     [2] = (lba >> 0x18) & 0xFF,
0140                     [3] = (lba >> 0x10) & 0xFF,
0141                     [4] = (lba >> 0x08) & 0xFF,
0142                     [5] = (lba >> 0x00) & 0xFF,
0143                     [8] = 1 };
0144   if (dev_index >= num_umsc_devs) return 0;
0145   umsc = &umsc_devs[dev_index];
0146   if (len < umsc->sector_size) return 0;
0147 
0148   if (umsc_bulk_scsi (umsc->devinfo->address,
0149                       umsc->ep_out, umsc->ep_in, cmd, 1, sector,
0150                       umsc->sector_size, umsc->maxpkt) != 0)
0151     return 0;
0152   return umsc->sector_size;
0153 }
0154 
0155 
0156 /* bit of a hack here since we don't have IPC yet */
0157 static task_id umsc_cur_task = 0, umsc_waitq = 0, umsc_thread_id = 0;
0158 static u32 umsc_cur_dev_index, umsc_cur_lba, umsc_cur_len;
0159 static u8 umsc_cur_sector[512];
0160 static sint umsc_cur_res;
0161 static u32 umsc_stack[1024] ALIGNED (0x1000);
0162 
0163 static void
0164 umsc_thread (void)
0165 {
0166   logger_printf ("umsc_thread: hello from 0x%x\n", str ());
0167   for (;;) {
0168     if (umsc_cur_task) {
0169       DLOG ("thread: read_sector for 0x%x (%d, %d, %p, %d)", umsc_cur_task,
0170             umsc_cur_dev_index, umsc_cur_lba, umsc_cur_sector,
0171             umsc_cur_len);
0172       umsc_cur_res = _umsc_read_sector (umsc_cur_dev_index, umsc_cur_lba,
0173                                         umsc_cur_sector, umsc_cur_len);
0174       wakeup (umsc_cur_task);
0175       umsc_cur_task = 0;
0176     }
0177     iovcpu_job_completion ();
0178   }
0179 }
0180 
0181 sint
0182 umsc_read_sector (uint dev_index, u32 lba, u8 *sector, uint len)
0183 {
0184   sint res;
0185 
0186   if (dev_index >= num_umsc_devs) return 0;
0187   if (len < umsc_devs[dev_index].sector_size) return 0;
0188 
0189   if (!mp_enabled || !umsc_thread_id) {
0190     return _umsc_read_sector (dev_index, lba, sector, len);
0191   }
0192 
0193   while (umsc_cur_task) {
0194     queue_append (&umsc_waitq, str ());
0195     schedule ();
0196   }
0197 
0198   umsc_cur_dev_index = dev_index;
0199   umsc_cur_lba = lba;
0200   umsc_cur_len = len;
0201 
0202   umsc_cur_task = str ();
0203 
0204   iovcpu_job_wakeup_for_me (umsc_thread_id);
0205 
0206   schedule ();
0207 
0208   memcpy (sector, umsc_cur_sector, sizeof (umsc_cur_sector));
0209   res = umsc_cur_res;
0210 
0211   wakeup_queue (&umsc_waitq);
0212 
0213   return res;
0214 }
0215 
0216 static bool
0217 umsc_probe (USB_DEVICE_INFO *info, USB_CFG_DESC *cfgd, USB_IF_DESC *ifd)
0218 {
0219   uint i, addr = info->address, ep_in=0, ep_out=0;
0220   USB_EPT_DESC *ep;
0221   uint last_lba, sector_size, maxpkt=64;
0222   uint8 conf[512];
0223   umsc_device_t *umsc;
0224 
0225   if (num_umsc_devs >= UMSC_MAX_DEVICES)
0226     return FALSE;
0227 
0228   if (ifd->bInterfaceClass != USB_MASS_STORAGE_CLASS ||
0229       ifd->bInterfaceProtocol != UMSC_PROTOCOL)
0230     return FALSE;
0231 
0232   ep = (USB_EPT_DESC *)(&ifd[1]);
0233 
0234   if (ep[0].wMaxPacketSize != ep[1].wMaxPacketSize) {
0235     DLOG ("endpoint packet sizes don't match!");
0236     return FALSE;
0237   }
0238   maxpkt = ep[0].wMaxPacketSize;
0239   for (i=0; i<ifd->bNumEndpoints; i++)
0240     if (ep[i].bEndpointAddress & 0x80)
0241       ep_in = ep[i].bEndpointAddress & 0x7F;
0242     else
0243       ep_out = ep[i].bEndpointAddress & 0x7F;
0244 
0245   if (!(ep_in && ep_out))
0246     return FALSE;
0247 
0248   DLOG ("detected device=%d ep_in=%d ep_out=%d maxpkt=%d",
0249         addr, ep_in, ep_out, maxpkt);
0250 
0251   usb_set_configuration (info, cfgd->bConfigurationValue);
0252   delay (50);
0253 
0254   testaddr = addr;
0255   testepin = ep_in;
0256   testepout = ep_out;
0257 
0258   {
0259     uint8 cmd[16] = {0x12,0,0,0,0x24,0,0,0,0,0,0,0};
0260     DLOG ("SENDING INQUIRY");
0261     if (umsc_bulk_scsi (addr, ep_out, ep_in, cmd, 1, conf, 0x24, maxpkt) != 0)
0262       return FALSE;
0263   }
0264 
0265   {
0266     uint8 cmd[16] = {0,0,0,0,0,0,0,0,0,0,0,0};
0267     DLOG ("SENDING TEST UNIT READY");
0268     if (umsc_bulk_scsi (addr, ep_out, ep_in, cmd, 1, conf, 0, maxpkt) != 0)
0269       return FALSE;
0270   }
0271 
0272   {
0273     uint8 cmd[16] = {0x03,0,0,0,0x24,0,0,0,0,0,0,0};
0274     DLOG ("SENDING REQUEST SENSE");
0275     if (umsc_bulk_scsi (addr, ep_out, ep_in, cmd, 1, conf, 0x24, maxpkt) != 0)
0276       return FALSE;
0277   }
0278 
0279   {
0280     uint8 cmd[16] = {0,0,0,0,0,0,0,0,0,0,0,0};
0281     DLOG ("SENDING TEST UNIT READY");
0282     if (umsc_bulk_scsi (addr, ep_out, ep_in, cmd, 1, conf, 0, maxpkt) != 0)
0283       return FALSE;
0284   }
0285 
0286   {
0287     uint8 cmd[16] = {0x03,0,0,0,0x24,0,0,0,0,0,0,0};
0288     DLOG ("SENDING REQUEST SENSE");
0289     if (umsc_bulk_scsi (addr, ep_out, ep_in, cmd, 1, conf, 0x24, maxpkt) != 0)
0290       return FALSE;
0291   }
0292 
0293   {
0294     uint8 cmd[16] = {0x25,0,0,0,0,0,0,0,0,0,0,0};
0295     DLOG ("SENDING READ CAPACITY");
0296     if (umsc_bulk_scsi (addr, ep_out, ep_in, cmd, 1, conf, 0x8, maxpkt) != 0)
0297       return FALSE;
0298     last_lba = conf[3] | conf[2] << 8 | conf[1] << 16 | conf[0] << 24;
0299     sector_size = conf[7] | conf[6] << 8 | conf[5] << 16 | conf[4] << 24;
0300     DLOG ("sector_size=0x%x last_lba=0x%x total_size=%d bytes",
0301             sector_size, last_lba, sector_size * (last_lba + 1));
0302   }
0303 
0304   memset (conf, 0, 512);
0305   {
0306     uint8 cmd[16] = { [0] = 0x28, [8] = 1 };
0307     DLOG ("SENDING READ (10)");
0308     if (umsc_bulk_scsi (addr, ep_out, ep_in, cmd, 1, conf, 512, maxpkt) != 0)
0309       return FALSE;
0310     DLOG ("read from sector 0: %.02X %.02X %.02X %.02X",
0311           conf[0], conf[1], conf[2], conf[3]);
0312     if (conf[510] == 0x55 && conf[511] == 0xAA)
0313       DLOG ("Found boot sector magic number");
0314   }
0315 
0316   umsc = &umsc_devs[num_umsc_devs];
0317   umsc->devinfo = info;
0318   umsc->sector_size = sector_size;
0319   umsc->last_lba = last_lba;
0320   umsc->ep_out = ep_out;
0321   umsc->ep_in = ep_in;
0322   umsc->maxpkt = maxpkt;
0323 
0324   DLOG ("Registered UMSC device index=%d", num_umsc_devs);
0325 
0326   num_umsc_devs++;
0327 
0328   umsc_thread_id =
0329     start_kernel_thread ((u32) umsc_thread, (u32) &umsc_stack[1023]);
0330   set_iovcpu (umsc_thread_id, IOVCPU_CLASS_USB | IOVCPU_CLASS_DISK);
0331 
0332   return TRUE;
0333 }
0334 
0335 static USB_DRIVER umsc_driver = {
0336   .probe = umsc_probe
0337 };
0338 
0339 extern bool
0340 usb_mass_storage_driver_init (void)
0341 {
0342   return usb_register_driver (&umsc_driver);
0343 }
0344 
0345 extern void
0346 umsc_tmr_test (void)
0347 {
0348   void uhci_show_regs (void);
0349   uint8 conf[16];
0350   uint addr = testaddr, ep_out = testepout, ep_in = testepin, maxpkt=64;
0351   uint last_lba, sector_size;
0352   {
0353     uint8 cmd[16] = {0x25,0,0,0,0,0,0,0,0,0,0,0};
0354     DLOG ("SENDING READ CAPACITY");
0355     umsc_bulk_scsi (addr, ep_out, ep_in, cmd, 1, conf, 0x8, maxpkt);
0356     last_lba = conf[3] | conf[2] << 8 | conf[1] << 16 | conf[0] << 24;
0357     sector_size = conf[7] | conf[6] << 8 | conf[5] << 16 | conf[4] << 24;
0358     DLOG ("sector_size=0x%x last_lba=0x%x total_size=%d bytes",
0359             sector_size, last_lba, sector_size * (last_lba + 1));
0360   }
0361 
0362   uhci_show_regs ();
0363 }
0364 
0365 #include "module/header.h"
0366 
0367 static const struct module_ops mod_ops = {
0368   .init = usb_mass_storage_driver_init
0369 };
0370 
0371 DEF_MODULE (usb___umsc, "USB mass storage driver", &mod_ops, {"usb"});
0372 
0373 
0374 /*
0375  * Local Variables:
0376  * indent-tabs-mode: nil
0377  * mode: C
0378  * c-file-style: "gnu"
0379  * c-basic-offset: 2
0380  * End:
0381  */
0382 
0383 /* vi: set et sw=2 sts=2: */