2 Copyright (C) 2006 by Michal Schulz
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this program; if not, write to the
17 Free Software Foundation, Inc.,
18 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include <aros/debug.h>
24 #include <aros/libcall.h>
25 #include <aros/asmcall.h>
28 #include <dos/dosextens.h>
31 #include <usb/usb_core.h>
37 #include <proto/oop.h>
38 #include <proto/dos.h>
40 BOOL
USBMSS_AddVolume(mss_unit_t
*unit
);
42 static unit_cache_t
*GetUnitFromCache(OOP_Class
*cl
, OOP_Object
*o
, uint8_t create
)
44 StorageData
*mss
= OOP_INST_DATA(cl
, o
);
45 struct mss_staticdata
*sd
= SD(cl
);
46 unit_cache_t
*unit
, *u
= NULL
;
50 char *product
, *manufacturer
, *serial
;
52 /* Get some basic info about the device */
53 OOP_GetAttr(o
, aHidd_USBDevice_ProductID
, &tmp
);
55 OOP_GetAttr(o
, aHidd_USBDevice_VendorID
, &tmp
);
57 OOP_GetAttr(o
, aHidd_USBDevice_ProductName
, &tmp
);
58 product
= (char *)tmp
;
59 OOP_GetAttr(o
, aHidd_USBDevice_ManufacturerName
, &tmp
);
60 manufacturer
= (char *)tmp
;
61 OOP_GetAttr(o
, aHidd_USBDevice_SerialNumber
, &tmp
);
64 D(bug("[MSS] GetUnitFromCache(%04x:%04x, '%s', '%s', '%s')\n", pid
, vid
, manufacturer
, product
, serial
));
66 /* Check the cache list. If unit was here already, use it */
67 ObtainSemaphore(&sd
->Lock
);
68 ForeachNode(&sd
->unitCache
, unit
)
70 if (pid
== unit
->productID
&& vid
== unit
->vendorID
)
72 if (strcmp(serial
, unit
->serialNumber
) == 0 &&
73 strcmp(product
, unit
->productName
) == 0 &&
74 strcmp(manufacturer
, unit
->manufacturerName
) == 0)
81 ReleaseSemaphore(&sd
->Lock
);
85 D(bug("[MSS] Found the device in cache already. Reassigning the old unit number %d\n",
90 D(bug("[MSS] Unit is new to the system. Creating new cache object\n"));
92 u
= AllocVecPooled(sd
->MemPool
, sizeof(unit_cache_t
));
99 u
->productName
= AllocVecPooled(sd
->MemPool
, strlen(product
) + 1);
101 strcpy(u
->productName
, product
);
104 u
->productName
= NULL
;
108 u
->manufacturerName
= AllocVecPooled(sd
->MemPool
, strlen(manufacturer
) + 1);
109 if (u
->manufacturerName
)
110 strcpy(u
->manufacturerName
, manufacturer
);
113 u
->manufacturerName
= NULL
;
117 u
->serialNumber
= AllocVecPooled(sd
->MemPool
, strlen(serial
) + 1);
119 strcpy(u
->serialNumber
, serial
);
122 u
->serialNumber
= NULL
;
124 ObtainSemaphore(&sd
->Lock
);
125 u
->unitNumber
= sd
->unitNum
;
126 sd
->unitNum
+= mss
->maxLUN
+ 1;
127 AddTail((struct List
*)&sd
->unitCache
, (struct Node
*)u
);
128 ReleaseSemaphore(&sd
->Lock
);
135 * Initialize the USBStorage instance
137 OOP_Object
*METHOD(Storage
, Root
, New
)
139 D(bug("[MSS] Storage::New()\n"));
141 /* Prevent the class from being expunged */
142 BASE(cl
->UserData
)->LibNode
.lib_OpenCnt
++;
144 o
= (OOP_Object
*)OOP_DoSuperMethod(cl
, o
, (OOP_Msg
) msg
);
147 StorageData
*mss
= OOP_INST_DATA(cl
, o
);
148 usb_config_descriptor_t cdesc
;
152 InitSemaphore(&mss
->lock
);
156 /* Get the interface number */
157 OOP_GetAttr(o
, aHidd_USBDevice_Interface
, &iface
);
159 /* Configure MSS device */
160 HIDD_USBDevice_Configure(o
, 0);
162 /* Get device and config descriptors */
163 D(bug("[MSS] Getting device descriptor\n"));
164 HIDD_USBDevice_GetDeviceDescriptor(o
, &mss
->ddesc
);
165 D(bug("[MSS] Getting initial config descriptor\n"));
166 HIDD_USBDevice_GetConfigDescriptor(o
, 0, &cdesc
);
167 /* How many LUNs this device supports? */
168 mss
->maxLUN
= HIDD_USBStorage_GetMaxLUN(o
);
169 if (mss
->maxLUN
> 15)
171 D(bug("[MSS] GetMaxLUN FAILED.\n"));
174 D(bug("[MSS] GetMaxLUN returns %d\n", mss
->maxLUN
));
176 if (AROS_LE2WORD(cdesc
.wTotalLength
))
177 mss
->cdesc
= AllocVecPooled(SD(cl
)->MemPool
, AROS_LE2WORD(cdesc
.wTotalLength
));
181 D(bug("[MSS] Getting config descriptor of size %d\n", AROS_LE2WORD(cdesc
.wTotalLength
)));
182 HIDD_USBDevice_GetDescriptor(o
, UDESC_CONFIG
, 0, AROS_LE2WORD(cdesc
.wTotalLength
), mss
->cdesc
);
184 D(bug("[MSS] Getting descriptor of interface %d\n", iface
));
185 mss
->iface
= HIDD_USBDevice_GetInterface(o
, iface
);
187 D(bug("[MSS] Interface has %d endpoints\n", mss
->iface
->bNumEndpoints
));
190 * Iterate through the possible endpoints and look for IN and OUT endpoints for BULK
193 for (i
=0; i
< mss
->iface
->bNumEndpoints
; i
++)
195 usb_endpoint_descriptor_t
*ep
;
196 ep
= HIDD_USBDevice_GetEndpoint(o
, iface
, i
);
198 D(bug("[MSS] endpoint %d: addr %02x, interval %02x, length %02x attr %02x maxpacket %04x\n", i
,
199 ep
->bEndpointAddress
,
203 AROS_LE2WORD(ep
->wMaxPacketSize
)));
205 /* Use only BULK endpoints */
206 if (UE_GET_XFERTYPE(ep
->bmAttributes
) == UE_BULK
)
208 if (!mss
->pipe_in
&& UE_GET_DIR(ep
->bEndpointAddress
) == UE_DIR_IN
)
210 D(bug("[MSS] IN endpoint found\n"));
212 mss
->pipe_in
= HIDD_USBDevice_CreatePipe(o
, PIPE_Bulk
, ep
->bEndpointAddress
, 0, AROS_LE2WORD(ep
->wMaxPacketSize
), 10000);
214 else if (!mss
->pipe_out
&& UE_GET_DIR(ep
->bEndpointAddress
) == UE_DIR_OUT
)
216 D(bug("[MSS] OUT endpoint found\n"));
218 mss
->pipe_out
= HIDD_USBDevice_CreatePipe(o
, PIPE_Bulk
, ep
->bEndpointAddress
, 0, AROS_LE2WORD(ep
->wMaxPacketSize
), 10000);
223 /* Pipes are there. Let's start the handler task */
224 if (mss
->pipe_in
&& mss
->pipe_out
)
229 HIDD_USBStorage_Reset(o
);
231 /* Get the unit from cache, in case it was used before */
232 mss
->cache
= GetUnitFromCache(cl
, o
, 0);
235 * Reuse the unit, i.e. do not create the tasks, just signal them and update
240 mss
->unitNum
= mss
->cache
->unitNumber
;
242 for (i
=0; i
<= mss
->maxLUN
; i
++)
244 mss
->unit
[i
] = (mss_unit_t
*)REMHEAD(&mss
->cache
->units
);
247 mss
->unit
[i
]->msu_object
= o
;
251 mss
->handler
[i
] = mss
->unit
[i
]->msu_handler
;
253 AddTail((struct List
*)&SD(cl
)->unitList
, (struct Node
*)mss
->unit
[i
]);
257 Signal(mss
->handler
[i
], SIGF_SINGLE
);
262 /* There was no such device used before. Create the cache element and tasks */
264 mss
->cache
= GetUnitFromCache(cl
, o
, 1);
265 mss
->unitNum
= mss
->cache
->unitNumber
;
268 * For each LUN create a separate task. The unit number will be created as combination of
269 * unitNum and the LUN: unit 0x00 is Unit 0 LUN 0, 0x12 is Unit 1 LUN 2.
271 for (i
=0; i
<= mss
->maxLUN
; i
++)
273 struct TagItem tags
[] = {
274 { TASKTAG_ARG1
, (IPTR
)cl
},
275 { TASKTAG_ARG2
, (IPTR
)o
},
277 { TASKTAG_ARG4
, (IPTR
)FindTask(NULL
) },
280 t
= AllocMem(sizeof(struct Task
), MEMF_PUBLIC
|MEMF_CLEAR
);
281 ml
= AllocMem(sizeof(struct MemList
) + 2*sizeof(struct MemEntry
), MEMF_PUBLIC
|MEMF_CLEAR
);
285 uint8_t *sp
= AllocMem(10240, MEMF_PUBLIC
|MEMF_CLEAR
);
286 uint8_t *name
= AllocMem(16, MEMF_PUBLIC
|MEMF_CLEAR
);
288 /* Create individual name */
289 snprintf(name
, 15, "USB MSS %02x.%x",mss
->unitNum
, i
);
292 t
->tc_SPUpper
= sp
+ 10240;
293 #if AROS_STACK_GROWS_DOWNWARDS
294 t
->tc_SPReg
= (char *)t
->tc_SPUpper
- SP_OFFSET
;
296 t
->tc_SPReg
= (char *)t
->tc_SPLower
+ SP_OFFSET
;
299 ml
->ml_NumEntries
= 3;
300 ml
->ml_ME
[0].me_Addr
= t
;
301 ml
->ml_ME
[0].me_Length
= sizeof(struct Task
);
302 ml
->ml_ME
[1].me_Addr
= sp
;
303 ml
->ml_ME
[1].me_Length
= 10240;
304 ml
->ml_ME
[2].me_Addr
= name
;
305 ml
->ml_ME
[2].me_Length
= 16;
307 NEWLIST(&t
->tc_MemEntry
);
308 ADDHEAD(&t
->tc_MemEntry
, &ml
->ml_Node
);
310 t
->tc_Node
.ln_Name
= name
;
311 t
->tc_Node
.ln_Type
= NT_TASK
;
312 t
->tc_Node
.ln_Pri
= 1; /* same priority as input.device */
314 /* Add task. It will get back in touch soon */
315 NewAddTask(t
, StorageTask
, NULL
, &tags
[0]);
316 /* Keep the initialization synchronous */
320 /* Detect partitions here? */
321 USBMSS_AddVolume(mss
->unit
[i
]);
329 D(bug("[MSS] Storage::New() = %p\n", o
));
332 BASE(cl
->UserData
)->LibNode
.lib_OpenCnt
--;
337 void METHOD(Storage
, Root
, Dispose
)
339 StorageData
*mss
= OOP_INST_DATA(cl
, o
);
340 OOP_Object
*drv
= NULL
;
343 struct Library
*base
= &BASE(cl
->UserData
)->LibNode
;
345 D(bug("[MSS] ::Dispose()\n"));
349 for (i
=0; i
<= mss
->maxLUN
; i
++)
353 REMOVE(mss
->unit
[i
]);
354 ADDTAIL(&mss
->cache
->units
, mss
->unit
[i
]);
358 Signal(mss
->handler
[i
], SIGF_SINGLE
);
362 HIDD_USBDevice_DeletePipe(o
, mss
->pipe_in
);
363 HIDD_USBDevice_DeletePipe(o
, mss
->pipe_out
);
366 OOP_GetAttr(o
, aHidd_USBDevice_Bus
, (intptr_t *)&drv
);
368 FreeVecPooled(SD(cl
)->MemPool
, mss
->cdesc
);
370 OOP_DoSuperMethod(cl
, o
, (OOP_Msg
)msg
);
375 BOOL
METHOD(Storage
, Hidd_USBStorage
, Reset
)
377 StorageData
*mss
= OOP_INST_DATA(cl
, o
);
378 USBDevice_Request req
;
381 ObtainSemaphore(&mss
->lock
);
382 OOP_GetAttr(o
, aHidd_USBDevice_InterfaceNumber
, &ifnr
);
384 req
.bmRequestType
= UT_WRITE_CLASS_INTERFACE
;
387 req
.wIndex
= AROS_WORD2LE(ifnr
);
390 HIDD_USBDevice_ControlMessage(o
, NULL
, &req
, NULL
, 0);
391 ReleaseSemaphore(&mss
->lock
);
396 uint8_t METHOD(Storage
, Hidd_USBStorage
, GetMaxLUN
)
398 USBDevice_Request req
;
402 OOP_GetAttr(o
, aHidd_USBDevice_InterfaceNumber
, &ifnr
);
404 req
.bmRequestType
= UT_READ_CLASS_INTERFACE
;
407 req
.wIndex
= AROS_WORD2LE(ifnr
);
410 if (HIDD_USBDevice_ControlMessage(o
, NULL
, &req
, &maxlun
, 1))
416 uint32_t METHOD(Storage
, Hidd_USBStorage
, DirectSCSI
)
418 StorageData
*mss
= OOP_INST_DATA(cl
, o
);
424 for (i
=0; i
< msg
->cmdLen
; i
++)
425 cbw
.CBWCB
[i
] = msg
->cmd
[i
];
427 cbw
.bCBWLUN
= msg
->lun
;
428 cbw
.dCBWSignature
= AROS_LONG2LE(CBW_SIGNATURE
);
429 cbw
.dCBWTag
= getTID(SD(cl
));
430 cbw
.dCBWDataTransferLength
= AROS_LONG2LE(msg
->dataLen
);
431 cbw
.bmCBWFlags
= msg
->dataLen
? (msg
->read
? CBW_FLAGS_IN
: CBW_FLAGS_OUT
) : 0;
432 cbw
.bCBWCBLength
= msg
->cmdLen
;
434 ObtainSemaphore(&mss
->lock
);
436 // D(bug("[MSS] DirectSCSI -> (%08x,%08x,%08x,%02x,%02x,%02x) @ %08x ",
437 // AROS_LONG2LE(cbw.dCBWSignature),
439 // AROS_LONG2LE(cbw.dCBWDataTransferLength),
440 // cbw.bCBWLUN, cbw.bmCBWFlags, cbw.bCBWCBLength,
441 // msg->dataLen? msg->data:0));
443 // for (i=0; i < msg->cmdLen; i++)
445 // D(bug("%02x%c", msg->cmd[i], i < (msg->cmdLen) ? ',':')'));
449 if (HIDD_USBDevice_BulkTransfer(o
, mss
->pipe_out
, &cbw
, 31))
454 HIDD_USBDevice_BulkTransfer(o
, mss
->pipe_in
, msg
->data
, msg
->dataLen
);
456 HIDD_USBDevice_BulkTransfer(o
, mss
->pipe_out
, msg
->data
, msg
->dataLen
);
459 if (HIDD_USBDevice_BulkTransfer(o
, mss
->pipe_in
, &csw
, 13))
461 // D(bug("[MSS] DirectSCSI <- (%08x,%08x,%08x,%02x)\n",
462 // AROS_LONG2LE(csw.dCSWSignature),
464 // AROS_LONG2LE(csw.dCSWDataResidue),
467 if (csw
.dCSWSignature
== AROS_LONG2LE(CSW_SIGNATURE
))
469 if (csw
.dCSWTag
== cbw
.dCBWTag
)
478 ReleaseSemaphore(&mss
->lock
);
483 BOOL
METHOD(Storage
, Hidd_USBStorage
, TestUnitReady
)
485 StorageData
*mss
= OOP_INST_DATA(cl
, o
);
486 uint8_t cmd
[12] = {0};
488 if (msg
->lun
> mss
->maxLUN
)
491 if (HIDD_USBStorage_DirectSCSI(o
, msg
->lun
, cmd
, 12, NULL
, 0, 1))
497 BOOL
METHOD(Storage
, Hidd_USBStorage
, Inquiry
)
499 StorageData
*mss
= OOP_INST_DATA(cl
, o
);
500 uint8_t cmd
[6] = {0x12, 0, 0, 0, 0, 0};
502 if (msg
->lun
> mss
->maxLUN
)
505 cmd
[4] = msg
->bufferLength
;
508 return HIDD_USBStorage_DirectSCSI(o
, msg
->lun
, cmd
, 6, msg
->buffer
, msg
->bufferLength
, 1);
513 BOOL
METHOD(Storage
, Hidd_USBStorage
, RequestSense
)
515 StorageData
*mss
= OOP_INST_DATA(cl
, o
);
516 uint8_t cmd
[6] = {0x03, 0, 0, 0, 0, 0};
518 if (msg
->lun
> mss
->maxLUN
)
521 cmd
[4] = msg
->bufferLength
;
524 return HIDD_USBStorage_DirectSCSI(o
, msg
->lun
, cmd
, 6, msg
->buffer
, msg
->bufferLength
, 1);
529 BOOL
METHOD(Storage
, Hidd_USBStorage
, Read
)
531 StorageData
*mss
= OOP_INST_DATA(cl
, o
);
532 uint8_t cmd
[10] = {0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0};
534 ULONG count
= msg
->count
;
535 ULONG block
= msg
->block
;
536 UBYTE
*buf
= msg
->buffer
;
538 if (msg
->lun
> mss
->maxLUN
)
541 // D(bug("[MSS] ::Read(%08x, %04x) => %p\n", msg->block, msg->count, msg->buffer));
547 ULONG cnt
= count
> 1024 ? 1024 : count
;
557 if (!HIDD_USBStorage_DirectSCSI(o
, msg
->lun
, cmd
, 10, buf
, cnt
* mss
->blocksize
[msg
->lun
], 1))
561 buf
+= cnt
* mss
->blocksize
[msg
->lun
];
571 BOOL
METHOD(Storage
, Hidd_USBStorage
, Write
)
573 StorageData
*mss
= OOP_INST_DATA(cl
, o
);
574 uint8_t cmd
[10] = {0x2a, 0, 0, 0, 0, 0, 0, 0, 0, 0};
576 ULONG count
= msg
->count
;
577 ULONG block
= msg
->block
;
578 UBYTE
*buf
= msg
->buffer
;
580 if (msg
->lun
> mss
->maxLUN
)
583 // D(bug("[MSS] ::Write(%08x, %04x) <= %p\n", msg->block, msg->count, msg->buffer));
589 ULONG cnt
= count
> 1024 ? 1024 : count
;
599 if (!HIDD_USBStorage_DirectSCSI(o
, msg
->lun
, cmd
, 10, msg
->buffer
, msg
->count
* mss
->blocksize
[msg
->lun
], 0))
603 buf
+= cnt
* mss
->blocksize
[msg
->lun
];
613 BOOL
METHOD(Storage
, Hidd_USBStorage
, ReadCapacity
)
615 StorageData
*mss
= OOP_INST_DATA(cl
, o
);
616 uint8_t cmd
[10] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0};
620 if (msg
->lun
> mss
->maxLUN
)
623 D(bug("[MSS] ReadCapacity(%d, %x, %x)\n", msg
->lun
, msg
->blockTotal
, msg
->blockSize
));
625 if (HIDD_USBStorage_DirectSCSI(o
, msg
->lun
, cmd
, 10, capacity
, 8, 1))
627 mss
->blocksize
[msg
->lun
] = (capacity
[4] << 24) | (capacity
[5] << 16) | (capacity
[6] << 8) | capacity
[7];
630 *msg
->blockTotal
= (capacity
[0] << 24) | (capacity
[1] << 16) | (capacity
[2] << 8) | capacity
[3];
632 *msg
->blockSize
= mss
->blocksize
[msg
->lun
];
640 for (i
=0; i
< 8; i
++)
641 D(bug("%02x ", capacity
[i
]));
648 static usb_interface_descriptor_t
*find_idesc(usb_config_descriptor_t
*cd
, int ifaceidx
, int altidx
)
650 char *p
= (char *)cd
;
651 char *end
= p
+ AROS_LE2WORD(cd
->wTotalLength
);
652 usb_interface_descriptor_t
*d
;
653 int curidx
, lastidx
, curaidx
= 0;
655 for (curidx
= lastidx
= -1; p
< end
; ) {
656 d
= (usb_interface_descriptor_t
*)p
;
658 if (d
->bLength
== 0) /* bad descriptor */
661 if (p
<= end
&& d
->bDescriptorType
== UDESC_INTERFACE
) {
662 if (d
->bInterfaceNumber
!= lastidx
) {
663 lastidx
= d
->bInterfaceNumber
;
668 if (ifaceidx
== curidx
&& altidx
== curaidx
)
675 static const char *subclass
[] = {
676 NULL
, "SCSI subset", "SFF-8020i", "QIC-157", "UFI", "SFF-8070i", "SCSI complete"
680 * The MatchCLID library call gets the device descriptor and full config
681 * descriptor (including the interface and endpoint descriptors). It will
682 * return CLID_Hidd_USBStorage if the requested interface conforms to Mass
685 AROS_LH3(void *, MatchCLID
,
686 AROS_LHA(usb_device_descriptor_t
*, dev
, A0
),
687 AROS_LHA(usb_config_descriptor_t
*, cfg
, A1
),
688 AROS_LHA(int, i
, D0
),
689 LIBBASETYPEPTR
, LIBBASE
, 5, Storage
)
695 D(bug("[MSS] MatchCLID(%p, %p)\n", dev
, cfg
));
697 if (dev
->bDeviceClass
== UDCLASS_IN_INTERFACE
)
699 usb_interface_descriptor_t
*iface
= find_idesc(cfg
, i
, 0);
701 D(bug("[MSS] UDCLASS_IN_INTERFACE OK. Checking interface %d\n", i
));
702 D(bug("[MSS] iface %d @ %p class %d subclass %d protocol %d\n", i
, iface
,
703 iface
->bInterfaceClass
, iface
->bInterfaceSubClass
, iface
->bInterfaceProtocol
));
705 if (iface
->bInterfaceClass
== 0x08)
707 D(bug("[MSS] Interface may be handled by Mass Storage Class\n"));
709 if (iface
->bInterfaceProtocol
== 0x50)
711 D(bug("[MSS] Lucky you. The device supports Bulk Only transport\n"));
713 if (iface
->bInterfaceSubClass
> 0 && iface
->bInterfaceSubClass
< 7)
715 D(bug("[MSS] Protocol used: %s\n", subclass
[iface
->bInterfaceSubClass
]));
717 if (iface
->bInterfaceSubClass
== 1 ||
718 iface
->bInterfaceSubClass
== 2 ||
719 iface
->bInterfaceSubClass
== 6)
720 clid
= CLID_Hidd_USBStorage
;
727 D(bug("[MSS] Pick me! Pick me! Pick me! I can handle it!\n"));