#include "common.h" #include #include #include #include #include #include #include #include #define MAX_URBS 10 #define DRIVER_UNUSED 0 #define DRIVER_ACTIVE 1 #define DRIVER_BOUND 2 #if 0 #define DEBUG_MSG(fmt, ...) ddekit_printf("%s : "fmt"\n", __func__, ##__VA_ARGS__ ) #else #define DEBUG_MSG(fmt, ...) #endif #undef DDEBUG #define DDEBUG 0 #include "debug.h" #define MAX_DEVS 256 #define MAX_DRIVERS 256 #define OK 0 #define INVAL_DEV (-1) struct my_context { unsigned urb_id; struct ddekit_usb_urb *d_urb; struct usb_urb *mx_urb; struct minix_usb_driver *drv; gid_t gid; }; struct minix_usb_driver { endpoint_t ep; /* address of the client */ int status; /* In what state is the client? */ int dev; /* which device is this driver handling */ unsigned interfaces; /* which interfaces of the device the driver is handling */ struct ddekit_usb_urb *urbs[MAX_URBS]; /* pending urbs */ unsigned long urb_id; /* generation of driver_local urb_ids */ }; struct minix_usb_device { struct ddekit_usb_dev *dev; unsigned int interfaces; }; static struct minix_usb_driver *find_driver(endpoint_t ep); static struct minix_usb_driver *find_unused_driver(void); static int add_to_pending_urbs(struct minix_usb_driver *drv, struct ddekit_usb_urb *urb); static int remove_from_pending_urbs(struct minix_usb_driver *drv, struct ddekit_usb_urb *urb); static struct ddekit_usb_urb * find_pending_urb(struct minix_usb_driver *drv, unsigned urb_id); static void register_driver(message *msg); static struct ddekit_usb_urb *ddekit_usb_urb_from_mx_urb(struct usb_urb *mx_urb); static void submit_urb(message *msg); static void cancle_urb(message *msg); static void get_info(message *msg); static void completion_callback(void *priv); static void prepare_devman_usbdev(struct ddekit_usb_dev * dev, int dev_id, unsigned int interfaces, struct devman_usb_dev *dudev); static void device_disconnect_callback(struct ddekit_usb_dev * dev); static int add_acl(int dev_id, unsigned interfaces, endpoint_t ep); static int del_acl(int dev_id, unsigned interaces, endpoint_t ep); static int handle_msg(message *msg); static void _ddekit_usb_thread(); static void device_connect_callback(struct ddekit_usb_dev * dev, unsigned int interfaces); char *_ddekit_usb_get_manufacturer(struct ddekit_usb_dev *ddev); char *_ddekit_usb_get_product(struct ddekit_usb_dev *ddev); char *_ddekit_usb_get_serial(struct ddekit_usb_dev *ddev); usb_device_descriptor_t *_ddekit_usb_get_device_desc(struct ddekit_usb_dev *ddev); usb_interface_descriptor_t *_ddekit_usb_get_interface_desc(struct ddekit_usb_dev *ddev, int inum); static ddekit_usb_malloc_fn my_malloc; static ddekit_usb_free_fn my_free; static struct minix_usb_driver gbl_drivers[MAX_DRIVERS]; static struct minix_usb_device _devices[MAX_DEVS]; static struct ddekit_usb_driver my_driver = { .completion = completion_callback, .connect = device_connect_callback, .disconnect = device_disconnect_callback, }; /***************************************************************************** * find_driver * ****************************************************************************/ static struct minix_usb_driver *find_driver(endpoint_t ep) { int i; for (i = 0; i < MAX_DRIVERS; i++ ){ if (gbl_drivers[i].ep == ep) { return &gbl_drivers[i]; } } return NULL; } /***************************************************************************** * find_unused_driver * ****************************************************************************/ static struct minix_usb_driver *find_unused_driver() { int i; for (i = 0; i < MAX_DRIVERS; i++ ){ if (gbl_drivers[i].status == DRIVER_UNUSED) { return &gbl_drivers[i]; } } return NULL; } /***************************************************************************** * add_to_pending_urbs * ****************************************************************************/ static int add_to_pending_urbs(struct minix_usb_driver *drv, struct ddekit_usb_urb *urb) { int i; for (i = 0; i < MAX_URBS; i++) { if (drv->urbs[i] == NULL) { drv->urbs[i] = urb; return 0; } } return -1; } /***************************************************************************** * remove_from_pending_urbs * ****************************************************************************/ static int remove_from_pending_urbs(struct minix_usb_driver *drv, struct ddekit_usb_urb *urb) { int i; for (i = 0; i < MAX_URBS; i++) { if (drv->urbs[i] == urb) { drv->urbs[i] = NULL; return 0; } } return -1; } /***************************************************************************** * find_pending_urb * ****************************************************************************/ static struct ddekit_usb_urb * find_pending_urb(struct minix_usb_driver *drv, unsigned urb_id) { int i; for (i = 0; i < MAX_URBS; i++) { if (((struct my_context*)drv->urbs[i]->priv)->urb_id == urb_id) { return drv->urbs[i]; } } return NULL; } /***************************************************************************** * register_driver * ****************************************************************************/ static void register_driver(message *msg) { endpoint_t ep = msg->m_source; struct minix_usb_driver *drv; msg->m_type=USB_REPLY; if ( (drv = find_driver(ep)) != NULL) { msg->m_type = USB_REPLY; msg->USB_RESULT = OK; ipc_send(ep,msg); } else { msg->m_type = USB_REPLY; msg->USB_RESULT = EPERM; ipc_send(ep,msg); return; } DEBUG_MSG("DRIVER %d registered \n" "Announcing device %d, interfaces 0x%x\n", ep, drv->dev, drv->interfaces); /* hand out the device */ msg->m_type = USB_ANNOUCE_DEV; msg->USB_DEV_ID = drv->dev; msg->USB_INTERFACES = drv->interfaces; ipc_send(ep, msg); } /***************************************************************************** * deregister_driver * ****************************************************************************/ static void deregister_driver(message *msg) { endpoint_t ep = msg->m_source; struct minix_usb_driver *drv; msg->m_type=USB_REPLY; if ( (drv = find_driver(ep)) == NULL) { DEBUG_MSG("Non-registered driver tries to unregister."); return; } else { /* do not accept requests for this client anymore! */ drv->status = DRIVER_UNUSED; msg->USB_RESULT = 0; asynsend3(ep, msg, AMF_NOREPLY); } } /***************************************************************************** * ddekit_usb_urb_from_mx_urb * ****************************************************************************/ static struct ddekit_usb_urb *ddekit_usb_urb_from_mx_urb(struct usb_urb *mx_urb) { /* * A helper function that generates (allocates and initializes) * a ddekit_usb_urb. */ struct ddekit_usb_urb *d_urb = (struct ddekit_usb_urb *) my_malloc(sizeof(struct ddekit_usb_urb)); if (d_urb == NULL) { return NULL; } d_urb->type = mx_urb->type; d_urb->direction = mx_urb->direction; d_urb->transfer_flags = mx_urb->transfer_flags; d_urb->size = mx_urb->size; d_urb->data = mx_urb->buffer; d_urb->interval = mx_urb->interval; d_urb->endpoint = mx_urb->endpoint; if (d_urb->type == USB_TRANSFER_CTL) { d_urb->setup_packet = mx_urb->setup_packet; } DEBUG_MSG("setup_package at %p", d_urb->setup_packet); if (d_urb->type == USB_TRANSFER_ISO) { d_urb->iso_desc = (struct ddekit_usb_iso_packet_desc *) mx_urb->buffer + mx_urb->iso_desc_offset; d_urb->number_of_packets = mx_urb->number_of_packets; } return d_urb; } /***************************************************************************** * submit_urb * ****************************************************************************/ static void submit_urb(message *msg) { /* * submit_urb * * Handles a submit_urb from a minix USB device driver. It copies the * usb_urb structure containing the buffers and generates and tries to * submit a ddekit_usb_urb. The reference to the ddekit_usb_urb is stored * in the driver structure in order to be able to cancle the URB on the * clients request. */ endpoint_t ep = msg->m_source; struct minix_usb_driver *drv; /* find driver */ if ( (drv = find_driver(ep)) == NULL) { DEBUG_MSG("Non-registered driver tries to send URB."); return; } else { int res; struct usb_urb *mx_urb = (struct usb_urb*) my_malloc(msg->USB_GRANT_SIZE+sizeof(void *)); if (mx_urb == NULL) { DEBUG_MSG("Can't allocat mem for mx_urb."); res = ENOMEM; goto out; } /* copy in URB */ res = sys_safecopyfrom(ep, msg->USB_GRANT_ID, 0, (vir_bytes) &mx_urb->dev_id, msg->USB_GRANT_SIZE); if (res != 0) { DEBUG_MSG("sys_safecopyfrom failed "); my_free(mx_urb); res = EINVAL; goto out; } DEBUG_MSG("URB type: %d", mx_urb->type); /* check if urb is valid */ if (mx_urb->dev_id >= MAX_DEVS && mx_urb->dev_id < 0) { DEBUG_MSG("Bogus device ID."); res = EINVAL; goto out; } /* create ddekit_usb_urb */ struct ddekit_usb_urb *d_urb = ddekit_usb_urb_from_mx_urb(mx_urb); d_urb->dev = _devices[drv->dev].dev; /* submit urb */ if (!d_urb) { res = ENOMEM; goto out; } struct my_context *ctx = (struct my_context *) my_malloc(sizeof(struct my_context)); if(!ctx) { res = ENOMEM; goto out; } ctx->drv = drv; ctx->urb_id = drv->urb_id++; mx_urb->urb_id = ctx->urb_id; ctx->mx_urb = mx_urb; ctx->d_urb = d_urb; ctx->gid = msg->USB_GRANT_ID; DEBUG_MSG("ctx: %p, urb_id: %d, d_urb: %p, mx_urb: %p, drv: %d, gid: %d ", ctx, ctx->urb_id, ctx->d_urb, ctx->mx_urb, ctx->drv, ctx->gid); d_urb->priv = ctx; res = add_to_pending_urbs(drv, d_urb); if (res == 0) { DEBUG_MSG("submitting urb..."); res = ddekit_usb_submit_urb(d_urb); if(res) { DEBUG_MSG("submitting urb failed (err: %d)", res); remove_from_pending_urbs(drv, d_urb); } } out: /* reply */ msg->m_type = USB_REPLY; msg->USB_URB_ID = mx_urb->urb_id; msg->USB_RESULT = res; if(res != 0) { if (mx_urb != NULL) { my_free(mx_urb); } if (ctx != NULL) { my_free(ctx); } if (d_urb != NULL) { my_free(d_urb); } } /* send reply */ ipc_send(ep, msg); } } /* * cancle_urb * * Cancels the submission of an URB identified by a URB_id */ /***************************************************************************** * cancle_urb * ****************************************************************************/ static void cancle_urb(message *msg) { endpoint_t ep = msg->m_source; struct minix_usb_driver *drv; msg->USB_RESULT = -1; msg->m_type = USB_REPLY; /* find driver */ if ( (drv = find_driver(ep)) == NULL) { DEBUG_MSG("Non-registered driver tries to cancel URB."); return; } else { struct ddekit_usb_urb *d_urb = NULL; d_urb = find_pending_urb(drv, msg->USB_URB_ID); if (d_urb != NULL) { ddekit_usb_cancle_urb(d_urb); msg->USB_RESULT = 0; } else { DEBUG_MSG("No URB to cancle"); msg->USB_RESULT = ENODEV; } } ipc_send(ep, msg); } /***************************************************************************** * get_info * *****************************************************************************/ static void get_info(message * msg) { struct minix_usb_driver * drv; endpoint_t ep; long info_type; long info_value; /* Read */ ep = msg->m_source; info_type = msg->USB_INFO_TYPE; info_value = msg->USB_INFO_VALUE; /* Reuse as reply */ msg->m_type = USB_REPLY; msg->USB_RESULT = -1; /* Try and find driver first */ if (NULL == (drv = find_driver(ep))) ddekit_printf("Non-registered driver tries to send info"); else /* Route info to device */ msg->USB_RESULT = ddekit_usb_info(_devices[drv->dev].dev, info_type, info_value); /* Reply */ ipc_send(ep, msg); } /***************************************************************************** * completion_callback * ****************************************************************************/ static void completion_callback(void *priv) { /* * completion_callback * * This is called by the DDE side. Here the data is copied back to * the driver and a message is send to inform the driver about the * completion. */ message msg; int res; struct my_context *ctx = (struct my_context *)priv; struct usb_urb *mx_urb = ctx->mx_urb; struct ddekit_usb_urb *d_urb = ctx->d_urb; struct minix_usb_driver *drv = ctx->drv; DEBUG_MSG("ctx: %p, urb_id: %d, d_urb: %p, mx_urb: %p, drv: %d, gid: %d ", ctx, ctx->urb_id, ctx->d_urb, ctx->mx_urb, ctx->drv, ctx->gid); /* update data in minix URB */ mx_urb->status = d_urb->status; mx_urb->actual_length = d_urb->actual_length; mx_urb->error_count = d_urb->error_count; mx_urb->transfer_flags = d_urb->transfer_flags; remove_from_pending_urbs(drv, d_urb); /* copy out URB */ res = sys_safecopyto(drv->ep, ctx->gid, 0, (vir_bytes) ((char*)mx_urb) + sizeof(void*), mx_urb->urb_size - sizeof(void*)); if (res != 0) { DEBUG_MSG("Copy out failed: %d", res); DEBUG_MSG(" URB ID: %d, Grant-ID: %d, Grant-size: %d", ctx->urb_id, ctx->gid, mx_urb->urb_size); } /* send message to client */ msg.m_type = USB_COMPLETE_URB; msg.USB_URB_ID = ctx->urb_id; asynsend3(drv->ep, &msg, AMF_NOREPLY); /* free stuff */ my_free(ctx); my_free(mx_urb); my_free(d_urb); } /***************************************************************************** * prepare_devman_usbdev * ****************************************************************************/ static void prepare_devman_usbdev (struct ddekit_usb_dev * dev, int dev_id, unsigned int interfaces, struct devman_usb_dev *dudev) { int j; int intf_count; /* * currently this is only implemented by stub driver */ usb_device_descriptor_t *desc = _ddekit_usb_get_device_desc(dev); dudev->manufacturer = _ddekit_usb_get_manufacturer(dev); dudev->product = _ddekit_usb_get_product(dev); dudev->serial = _ddekit_usb_get_serial(dev); dudev->desc = desc; intf_count = 0; for (j=0; j < 32; j++) { if (interfaces & (1 << j)) { dudev->interfaces[intf_count++].desc = _ddekit_usb_get_interface_desc(dev, j); } } dudev->intf_count = intf_count; dudev->dev_id = dev_id; } /***************************************************************************** * device_connect_callback * ****************************************************************************/ static void device_connect_callback (struct ddekit_usb_dev * dev, unsigned int interfaces) { int i, res; /* add to device list */ for (i=0; i < MAX_DEVS; i++) { if (_devices[i].dev == NULL) break; } if (i >= MAX_DEVS) { DEBUG_MSG("Too much devices..."); } else { _devices[i].dev = dev; _devices[i].interfaces = (1 << interfaces); } struct devman_usb_dev *dudev; dudev = devman_usb_device_new(i); prepare_devman_usbdev(dev, i, interfaces, dudev); if (dudev == NULL) { /* TODO: ERROR */ printf("ERROR: !"); } ddekit_usb_dev_set_data(dev, dudev); res = devman_usb_device_add(dudev); if (res != 0) { /* TODO: Error*/ printf("ERROR!"); } } /***************************************************************************** * device_disconnect_callback * ****************************************************************************/ static void device_disconnect_callback(struct ddekit_usb_dev * dev) { int i; /* remove ACL entry */ for (i = 0; i< MAX_DRIVERS; i++) { if (gbl_drivers[i].dev != INVAL_DEV && _devices[gbl_drivers[i].dev].dev == dev) { struct minix_usb_driver *drv = &gbl_drivers[i]; drv->ep = 0; drv->status = DRIVER_UNUSED; drv->dev = INVAL_DEV; } } for (i=0; i < MAX_DEVS; i++) { if (_devices[i].dev == dev) { _devices[i].dev = NULL; _devices[i].interfaces = 0; } } /* get the devman device */ struct devman_usb_dev * dudev = NULL; dudev = ddekit_usb_dev_get_data(dev); if (dudev == NULL) { /* TODO: error */ } devman_usb_device_remove(dudev); /* free the devman dev */ devman_usb_device_delete(dudev); } /***************************************************************************** * add_acl * ****************************************************************************/ static int add_acl(int dev_id, unsigned interfaces, endpoint_t ep) { /* * This functions binds a specific USB interface to a client. */ int i; struct minix_usb_driver *drv; if (_devices[dev_id].dev == NULL) { /* if no device with that ID */ return ENODEV; } /* is the device allready given to a client*/ for (i = 0; i< MAX_DRIVERS; i++) { if (gbl_drivers[i].status != DRIVER_UNUSED && gbl_drivers[i].dev == dev_id) { printf("devid: %d\n", dev_id); return EBUSY; } } /* bind device to client */ drv = find_unused_driver(); if (drv == NULL) { return ENOMEM; } drv->status = DRIVER_BOUND; drv->dev = dev_id; drv->interfaces = 1 << interfaces; drv->ep = ep; drv->urb_id = 0; return OK; } /***************************************************************************** * del_acl * ****************************************************************************/ static int del_acl(int dev_id, unsigned interfaces, endpoint_t ep) { struct minix_usb_driver *drv; int dev, withdraw = 0; message msg; /* find driver */ drv = find_driver(ep); if (drv == NULL) { return ENOENT; } dev = drv->dev; if (drv->status == DRIVER_ACTIVE) { withdraw = 1; } drv->ep = 0; drv->status = DRIVER_UNUSED; drv->dev = INVAL_DEV; if (withdraw) { msg.m_type = USB_WITHDRAW_DEV; msg.USB_DEV_ID = dev; asynsend3(ep, &msg, AMF_NOREPLY); } return 0; } /***************************************************************************** * handle_msg * ****************************************************************************/ static int handle_msg(message *msg) { /* * handle_msg * * The dispatcher for USB related messages. */ switch(msg->m_type) { case USB_RQ_INIT: register_driver(msg); return 1; case USB_RQ_DEINIT: deregister_driver(msg); return 1; case USB_RQ_SEND_URB: submit_urb(msg); return 1; case USB_RQ_CANCEL_URB: cancle_urb(msg); return 1; case USB_RQ_SEND_INFO: get_info(msg); return 1; default: return 0; } } /***************************************************************************** * devman_tread * ****************************************************************************/ static void devman_thread(void *unused) { struct ddekit_minix_msg_q *mq = ddekit_minix_create_msg_q(DEVMAN_BASE, DEVMAN_BASE + 0xff); int ipc_status; message m; while (1) { ddekit_minix_rcv(mq, &m, &ipc_status); devman_handle_msg(&m); } } /***************************************************************************** * _ddekit_usb_thread * ****************************************************************************/ static void _ddekit_usb_thread(void * unused) { struct ddekit_minix_msg_q *mq = ddekit_minix_create_msg_q(USB_BASE, USB_BASE + 0xff); message m; int ipc_status; /* create devman thread */ ddekit_thread_t * dmth; dmth = ddekit_thread_create(devman_thread, NULL, "devman_thread"); while (1) { ddekit_minix_rcv(mq, &m, &ipc_status); handle_msg(&m); } } /***************************************************************************** * bind_cb * ****************************************************************************/ static int bind_cb (struct devman_usb_bind_cb_data *data, endpoint_t ep) { if(data) { return add_acl(data->dev_id, data->interface, ep); } else { printf("warning: missing cb_data!\n"); return EINVAL; } } /***************************************************************************** * unbind_cb * ****************************************************************************/ static int unbind_cb (struct devman_usb_bind_cb_data *data, endpoint_t ep) { if(data) { return del_acl(data->dev_id, data->interface, ep); } else { printf("warning: missing cb_data!\n"); return EINVAL; } } /***************************************************************************** * ddekit_usb_server_init * ****************************************************************************/ void ddekit_usb_server_init() { int i; /* * this function has to be called inside the context of an dedicated * DDELinux thread */ devman_usb_init(bind_cb, unbind_cb); ddekit_usb_init(&my_driver, &my_malloc, &my_free); for (i = 0; i< MAX_DRIVERS; i++) { gbl_drivers[i].dev = DRIVER_UNUSED; gbl_drivers[i].dev = INVAL_DEV; } _ddekit_usb_thread(NULL); }