Restructure usbms as a preparation for hotplug

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-06-02 19:52:17 +02:00
parent 4f034dd24d
commit 440ab68551
3 changed files with 227 additions and 169 deletions

View file

@ -21,6 +21,7 @@
#include <grub/mm.h> #include <grub/mm.h>
#include <grub/usb.h> #include <grub/usb.h>
#include <grub/misc.h> #include <grub/misc.h>
#include <grub/list.h>
static grub_usb_controller_dev_t grub_usb_list; static grub_usb_controller_dev_t grub_usb_list;
@ -232,3 +233,53 @@ grub_usb_device_initialize (grub_usb_device_t dev)
return err; return err;
} }
struct grub_usb_attach_desc *attach_hooks;
void
grub_usb_register_attach_hook_class (struct grub_usb_attach_desc *desc)
{
auto int usb_iterate (grub_usb_device_t dev);
int usb_iterate (grub_usb_device_t usbdev)
{
struct grub_usb_desc_device *descdev = &usbdev->descdev;
int i;
if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0
|| descdev->configcnt == 0)
return 0;
/* XXX: Just check configuration 0 for now. */
for (i = 0; i < usbdev->config[0].descconf->numif; i++)
{
struct grub_usb_desc_if *interf;
interf = usbdev->config[0].interf[i].descif;
grub_dprintf ("usb", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n",
i, interf->class, interf->subclass, interf->protocol);
if (usbdev->config[0].interf[i].attached)
continue;
if (interf->class != desc->class)
continue;
if (desc->hook (usbdev, 0, i))
usbdev->config[0].interf[i].attached = 1;
}
return 0;
}
desc->next = attach_hooks;
attach_hooks = desc;
grub_usb_iterate (usb_iterate);
}
void
grub_usb_unregister_attach_hook_class (struct grub_usb_attach_desc *desc)
{
grub_list_remove (GRUB_AS_LIST_P (&attach_hooks), GRUB_AS_LIST (desc));
}

View file

@ -52,20 +52,19 @@ struct grub_usbms_dev
int luns; int luns;
int config;
int interface; int interface;
struct grub_usb_desc_endp *in; struct grub_usb_desc_endp *in;
struct grub_usb_desc_endp *out; struct grub_usb_desc_endp *out;
int in_maxsz; int in_maxsz;
int out_maxsz; int out_maxsz;
struct grub_usbms_dev *next;
}; };
typedef struct grub_usbms_dev *grub_usbms_dev_t; typedef struct grub_usbms_dev *grub_usbms_dev_t;
static grub_usbms_dev_t grub_usbms_dev_list; /* FIXME: remove limit. */
#define MAX_USBMS_DEVICES 128
static int devcnt; static grub_usbms_dev_t grub_usbms_devices[MAX_USBMS_DEVICES];
static grub_err_t static grub_err_t
grub_usbms_reset (grub_usb_device_t dev, int interface) grub_usbms_reset (grub_usb_device_t dev, int interface)
@ -74,58 +73,53 @@ grub_usbms_reset (grub_usb_device_t dev, int interface)
} }
static void static void
grub_usbms_finddevs (void) grub_usbms_detach (grub_usb_device_t usbdev, int config, int interface)
{ {
auto int usb_iterate (grub_usb_device_t dev); unsigned i;
for (i = 0; i < ARRAY_SIZE (grub_usbms_devices); i++)
int usb_iterate (grub_usb_device_t usbdev) if (grub_usbms_devices[i] && grub_usbms_devices[i]->dev == usbdev
&& grub_usbms_devices[i]->interface == interface
&& grub_usbms_devices[i]->config == config)
{ {
grub_usb_err_t err; grub_free (grub_usbms_devices[i]);
struct grub_usb_desc_device *descdev = &usbdev->descdev; grub_usbms_devices[i] = 0;
int i; }
}
if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0 static int
|| descdev->configcnt == 0) grub_usbms_attach (grub_usb_device_t usbdev, int configno, int interfno)
return 0; {
struct grub_usb_desc_if *interf
/* XXX: Just check configuration 0 for now. */ = usbdev->config[configno].interf[interfno].descif;
for (i = 0; i < usbdev->config[0].descconf->numif; i++)
{
struct grub_usbms_dev *usbms;
struct grub_usb_desc_if *interf;
int j; int j;
grub_uint8_t luns = 0; grub_uint8_t luns = 0;
unsigned curnum;
grub_usb_err_t err;
grub_dprintf ("usbms", "alive\n"); for (curnum = 0; curnum < ARRAY_SIZE (grub_usbms_devices); curnum++)
if (!grub_usbms_devices[curnum])
break;
interf = usbdev->config[0].interf[i].descif; if (curnum == ARRAY_SIZE (grub_usbms_devices))
return 0;
/* If this is not a USB Mass Storage device with a supported interf = usbdev->config[configno].interf[interfno].descif;
protocol, just skip it. */
grub_dprintf ("usbms", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n",
i, interf->class, interf->subclass, interf->protocol);
if (interf->class != GRUB_USB_CLASS_MASS_STORAGE if ((interf->subclass != GRUB_USBMS_SUBCLASS_BULK
|| ( interf->subclass != GRUB_USBMS_SUBCLASS_BULK &&
/* Experimental support of RBC, MMC-2, UFI, SFF-8070i devices */ /* Experimental support of RBC, MMC-2, UFI, SFF-8070i devices */
interf->subclass != GRUB_USBMS_SUBCLASS_RBC && && interf->subclass != GRUB_USBMS_SUBCLASS_RBC
interf->subclass != GRUB_USBMS_SUBCLASS_MMC2 && && interf->subclass != GRUB_USBMS_SUBCLASS_MMC2
interf->subclass != GRUB_USBMS_SUBCLASS_UFI && && interf->subclass != GRUB_USBMS_SUBCLASS_UFI
interf->subclass != GRUB_USBMS_SUBCLASS_SFF8070 ) && interf->subclass != GRUB_USBMS_SUBCLASS_SFF8070 )
|| interf->protocol != GRUB_USBMS_PROTOCOL_BULK) || interf->protocol != GRUB_USBMS_PROTOCOL_BULK)
{ return 0;
continue;
}
grub_dprintf ("usbms", "alive\n"); grub_usbms_devices[curnum] = grub_zalloc (sizeof (struct grub_usbms_dev));
if (! grub_usbms_devices[curnum])
return 0;
devcnt++; grub_usbms_devices[curnum]->dev = usbdev;
usbms = grub_zalloc (sizeof (struct grub_usbms_dev)); grub_usbms_devices[curnum]->interface = interfno;
if (! usbms)
return 1;
usbms->dev = usbdev;
usbms->interface = i;
grub_dprintf ("usbms", "alive\n"); grub_dprintf ("usbms", "alive\n");
@ -134,29 +128,30 @@ grub_usbms_finddevs (void)
for (j = 0; j < interf->endpointcnt; j++) for (j = 0; j < interf->endpointcnt; j++)
{ {
struct grub_usb_desc_endp *endp; struct grub_usb_desc_endp *endp;
endp = &usbdev->config[0].interf[i].descendp[j]; endp = &usbdev->config[0].interf[interfno].descendp[j];
if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2) if ((endp->endp_addr & 128) && (endp->attrib & 3) == 2)
{ {
/* Bulk IN endpoint. */ /* Bulk IN endpoint. */
usbms->in = endp; grub_usbms_devices[curnum]->in = endp;
/* Clear Halt is not possible yet! */ /* Clear Halt is not possible yet! */
/* grub_usb_clear_halt (usbdev, endp->endp_addr); */ /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
usbms->in_maxsz = endp->maxpacket; grub_usbms_devices[curnum]->in_maxsz = endp->maxpacket;
} }
else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2) else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2)
{ {
/* Bulk OUT endpoint. */ /* Bulk OUT endpoint. */
usbms->out = endp; grub_usbms_devices[curnum]->out = endp;
/* Clear Halt is not possible yet! */ /* Clear Halt is not possible yet! */
/* grub_usb_clear_halt (usbdev, endp->endp_addr); */ /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
usbms->out_maxsz = endp->maxpacket; grub_usbms_devices[curnum]->out_maxsz = endp->maxpacket;
} }
} }
if (!usbms->in || !usbms->out) if (!grub_usbms_devices[curnum]->in || !grub_usbms_devices[curnum]->out)
{ {
grub_free (usbms); grub_free (grub_usbms_devices[curnum]);
grub_usbms_devices[curnum] = 0;
return 0; return 0;
} }
@ -166,32 +161,30 @@ grub_usbms_finddevs (void)
grub_usb_set_configuration (usbdev, 1); grub_usb_set_configuration (usbdev, 1);
/* Query the amount of LUNs. */ /* Query the amount of LUNs. */
err = grub_usb_control_msg (usbdev, 0xA1, 254, err = grub_usb_control_msg (usbdev, 0xA1, 254, 0, interfno, 1, (char *) &luns);
0, i, 1, (char *) &luns);
if (err) if (err)
{ {
/* In case of a stall, clear the stall. */ /* In case of a stall, clear the stall. */
if (err == GRUB_USB_ERR_STALL) if (err == GRUB_USB_ERR_STALL)
{ {
grub_usb_clear_halt (usbdev, usbms->in->endp_addr); grub_usb_clear_halt (usbdev, grub_usbms_devices[curnum]->in->endp_addr);
grub_usb_clear_halt (usbdev, usbms->out->endp_addr); grub_usb_clear_halt (usbdev, grub_usbms_devices[curnum]->out->endp_addr);
} }
/* Just set the amount of LUNs to one. */ /* Just set the amount of LUNs to one. */
grub_errno = GRUB_ERR_NONE; grub_errno = GRUB_ERR_NONE;
usbms->luns = 1; grub_usbms_devices[curnum]->luns = 1;
} }
else else
/* luns = 0 means one LUN with ID 0 present ! */ /* luns = 0 means one LUN with ID 0 present ! */
/* We get from device not number of LUNs but highest /* We get from device not number of LUNs but highest
* LUN number. LUNs are numbered from 0, * LUN number. LUNs are numbered from 0,
* i.e. number of LUNs is luns+1 ! */ * i.e. number of LUNs is luns+1 ! */
usbms->luns = luns + 1; grub_usbms_devices[curnum]->luns = luns + 1;
grub_dprintf ("usbms", "alive\n"); grub_dprintf ("usbms", "alive\n");
usbms->next = grub_usbms_dev_list; usbdev->config[configno].interf[interfno].detach_hook = grub_usbms_detach;
grub_usbms_dev_list = usbms;
#if 0 /* All this part should be probably deleted. #if 0 /* All this part should be probably deleted.
* This make trouble on some devices if they are not in * This make trouble on some devices if they are not in
@ -206,17 +199,7 @@ grub_usbms_finddevs (void)
grub_usb_clear_halt (usbdev, usbms->out->endp_addr); grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
#endif #endif
return 0; return 1;
}
grub_dprintf ("usbms", "alive\n");
return 0;
}
grub_dprintf ("usbms", "alive\n");
grub_usb_iterate (usb_iterate);
grub_dprintf ("usbms", "alive\n");
} }
@ -224,21 +207,20 @@ grub_usbms_finddevs (void)
static int static int
grub_usbms_iterate (int (*hook) (const char *name, int luns)) grub_usbms_iterate (int (*hook) (const char *name, int luns))
{ {
grub_usbms_dev_t p; unsigned i;
int cnt = 0;
for (p = grub_usbms_dev_list; p; p = p->next) for (i = 0; i < ARRAY_SIZE (grub_usbms_devices); i++)
if (grub_usbms_devices[i])
{ {
char *devname; char *devname;
devname = grub_xasprintf ("usb%d", cnt); devname = grub_xasprintf ("usb%d", i);
if (hook (devname, p->luns)) if (hook (devname, grub_usbms_devices[i]->luns))
{ {
grub_free (devname); grub_free (devname);
return 1; return 1;
} }
grub_free (devname); grub_free (devname);
cnt++;
} }
return 0; return 0;
@ -400,34 +382,24 @@ grub_usbms_write (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
static grub_err_t static grub_err_t
grub_usbms_open (const char *name, struct grub_scsi *scsi) grub_usbms_open (const char *name, struct grub_scsi *scsi)
{ {
grub_usbms_dev_t p;
int devnum; int devnum;
int i = 0;
if (grub_strncmp (name, "usb", 3)) if (grub_strncmp (name, "usb", 3))
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
"not a USB Mass Storage device"); "not a USB Mass Storage device");
devnum = grub_strtoul (name + 3, NULL, 10); devnum = grub_strtoul (name + 3, NULL, 10);
for (p = grub_usbms_dev_list; p; p = p->next) if (!grub_usbms_devices[devnum])
{ return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
/* Check if this is the devnumth device. */ "not a USB Mass Storage device");
if (devnum == i)
{ scsi->data = grub_usbms_devices[devnum];
scsi->data = p;
scsi->name = grub_strdup (name); scsi->name = grub_strdup (name);
scsi->luns = p->luns; scsi->luns = grub_usbms_devices[devnum]->luns;
if (! scsi->name) if (! scsi->name)
return grub_errno; return grub_errno;
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
}
i++;
}
return grub_error (GRUB_ERR_UNKNOWN_DEVICE,
"not a USB Mass Storage device");
} }
static void static void
@ -446,13 +418,28 @@ static struct grub_scsi_dev grub_usbms_dev =
.write = grub_usbms_write .write = grub_usbms_write
}; };
struct grub_usb_attach_desc attach_hook =
{
.class = GRUB_USB_CLASS_MASS_STORAGE,
.hook = grub_usbms_attach
};
GRUB_MOD_INIT(usbms) GRUB_MOD_INIT(usbms)
{ {
grub_usbms_finddevs (); grub_usb_register_attach_hook_class (&attach_hook);
grub_scsi_dev_register (&grub_usbms_dev); grub_scsi_dev_register (&grub_usbms_dev);
} }
GRUB_MOD_FINI(usbms) GRUB_MOD_FINI(usbms)
{ {
unsigned i;
for (i = 0; i < ARRAY_SIZE (grub_usbms_devices); i++)
{
grub_usbms_devices[i]->dev->config[grub_usbms_devices[i]->config]
.interf[grub_usbms_devices[i]->interface].detach_hook = 0;
grub_usbms_devices[i]->dev->config[grub_usbms_devices[i]->config]
.interf[grub_usbms_devices[i]->interface].attached = 0;
}
grub_usb_unregister_attach_hook_class (&attach_hook);
grub_scsi_dev_unregister (&grub_usbms_dev); grub_scsi_dev_unregister (&grub_usbms_dev);
} }

View file

@ -125,6 +125,13 @@ struct grub_usb_interface
struct grub_usb_desc_if *descif; struct grub_usb_desc_if *descif;
struct grub_usb_desc_endp *descendp; struct grub_usb_desc_endp *descendp;
/* A driver is handling this interface. Do we need to support multiple drivers
for single interface?
*/
int attached;
void (*detach_hook) (struct grub_usb_device *dev, int config, int interface);
}; };
struct grub_usb_configuration struct grub_usb_configuration
@ -207,4 +214,17 @@ grub_usb_get_config_interface (struct grub_usb_desc_config *config)
return interf; return interf;
} }
typedef int (*grub_usb_attach_hook_class) (grub_usb_device_t usbdev,
int configno, int interfno);
struct grub_usb_attach_desc
{
struct grub_usb_attach_desc *next;
int class;
grub_usb_attach_hook_class hook;
};
void grub_usb_register_attach_hook_class (struct grub_usb_attach_desc *desc);
void grub_usb_unregister_attach_hook_class (struct grub_usb_attach_desc *desc);
#endif /* GRUB_USB_H */ #endif /* GRUB_USB_H */