1 /******************************************************************************
2 * arch/xen/drivers/usbif/backend/interface.c
4 * USB device interface management.
6 * by Mark Williamson, Copyright (c) 2004
10 /******************************************************************************
11 * arch/xen/drivers/blkif/backend/interface.c
13 * Block-device interface management.
15 * Copyright (c) 2004, Keir Fraser
20 #define USBIF_HASHSZ 1024
21 #define USBIF_HASH(_d) (((int)(_d))&(USBIF_HASHSZ-1))
23 static kmem_cache_t *usbif_priv_cachep;
24 static usbif_priv_t *usbif_priv_hash[USBIF_HASHSZ];
26 usbif_priv_t *usbif_find(domid_t domid)
28 usbif_priv_t *up = usbif_priv_hash[USBIF_HASH(domid)];
29 while ( (up != NULL ) && ( up->domid != domid ) )
34 static void __usbif_disconnect_complete(void *arg)
36 usbif_priv_t *usbif = (usbif_priv_t *)arg;
38 usbif_be_disconnect_t disc;
41 * These can't be done in usbif_disconnect() because at that point there
42 * may be outstanding requests at the device whose asynchronous responses
43 * must still be notified to the remote driver.
45 unbind_evtchn_from_irq(usbif->evtchn);
46 vfree(usbif->usb_ring.sring);
48 /* Construct the deferred response message. */
49 cmsg.type = CMSG_USBIF_BE;
50 cmsg.subtype = CMSG_USBIF_BE_DISCONNECT;
51 cmsg.id = usbif->disconnect_rspid;
52 cmsg.length = sizeof(usbif_be_disconnect_t);
53 disc.domid = usbif->domid;
54 disc.status = USBIF_BE_STATUS_OKAY;
55 memcpy(cmsg.msg, &disc, sizeof(disc));
58 * Make sure message is constructed /before/ status change, because
59 * after the status change the 'usbif' structure could be deallocated at
60 * any time. Also make sure we send the response /after/ status change,
61 * as otherwise a subsequent CONNECT request could spuriously fail if
62 * another CPU doesn't see the status change yet.
65 if ( usbif->status != DISCONNECTING )
67 usbif->status = DISCONNECTED;
70 /* Send the successful response. */
71 ctrl_if_send_response(&cmsg);
74 void usbif_disconnect_complete(usbif_priv_t *up)
76 INIT_WORK(&up->work, __usbif_disconnect_complete, (void *)up);
77 schedule_work(&up->work);
80 void usbif_create(usbif_be_create_t *create)
82 domid_t domid = create->domid;
83 usbif_priv_t **pup, *up;
85 if ( (up = kmem_cache_alloc(usbif_priv_cachep, GFP_KERNEL)) == NULL )
87 DPRINTK("Could not create usbif: out of memory\n");
88 create->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
92 memset(up, 0, sizeof(*up));
94 up->status = DISCONNECTED;
95 spin_lock_init(&up->usb_ring_lock);
96 atomic_set(&up->refcnt, 0);
98 pup = &usbif_priv_hash[USBIF_HASH(domid)];
99 while ( *pup != NULL )
101 if ( (*pup)->domid == domid )
103 create->status = USBIF_BE_STATUS_INTERFACE_EXISTS;
104 kmem_cache_free(usbif_priv_cachep, up);
107 pup = &(*pup)->hash_next;
110 up->hash_next = *pup;
113 create->status = USBIF_BE_STATUS_OKAY;
116 void usbif_destroy(usbif_be_destroy_t *destroy)
118 domid_t domid = destroy->domid;
119 usbif_priv_t **pup, *up;
121 pup = &usbif_priv_hash[USBIF_HASH(domid)];
122 while ( (up = *pup) != NULL )
124 if ( up->domid == domid )
126 if ( up->status != DISCONNECTED )
127 goto still_connected;
130 pup = &up->hash_next;
133 destroy->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
137 destroy->status = USBIF_BE_STATUS_INTERFACE_CONNECTED;
141 *pup = up->hash_next;
142 usbif_release_ports(up);
143 kmem_cache_free(usbif_priv_cachep, up);
144 destroy->status = USBIF_BE_STATUS_OKAY;
147 void usbif_connect(usbif_be_connect_t *connect)
149 domid_t domid = connect->domid;
150 unsigned int evtchn = connect->evtchn;
151 unsigned long shmem_frame = connect->shmem_frame;
152 struct vm_struct *vma;
156 usbif_sring_t *sring;
158 up = usbif_find(domid);
159 if ( unlikely(up == NULL) )
161 DPRINTK("usbif_connect attempted for non-existent usbif (%u)\n",
163 connect->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
167 if ( (vma = get_vm_area(PAGE_SIZE, VM_IOREMAP)) == NULL )
169 connect->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
173 prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED);
174 error = direct_remap_area_pages(&init_mm, VMALLOC_VMADDR(vma->addr),
175 shmem_frame<<PAGE_SHIFT, PAGE_SIZE,
179 if ( error == -ENOMEM )
180 connect->status = USBIF_BE_STATUS_OUT_OF_MEMORY;
181 else if ( error == -EFAULT )
182 connect->status = USBIF_BE_STATUS_MAPPING_ERROR;
184 connect->status = USBIF_BE_STATUS_ERROR;
189 if ( up->status != DISCONNECTED )
191 connect->status = USBIF_BE_STATUS_INTERFACE_CONNECTED;
196 sring = (usbif_sring_t *)vma->addr;
197 SHARED_RING_INIT(sring);
198 BACK_RING_INIT(&up->usb_ring, sring, PAGE_SIZE);
201 up->irq = bind_evtchn_to_irq(evtchn);
202 up->shmem_frame = shmem_frame;
203 up->status = CONNECTED;
206 request_irq(up->irq, usbif_be_int, 0, "usbif-backend", up);
208 connect->status = USBIF_BE_STATUS_OKAY;
211 /* Remove URBs for this interface before destroying it. */
212 void usbif_deschedule(usbif_priv_t *up)
214 remove_from_usbif_list(up);
217 int usbif_disconnect(usbif_be_disconnect_t *disconnect, u8 rsp_id)
219 domid_t domid = disconnect->domid;
222 up = usbif_find(domid);
223 if ( unlikely(up == NULL) )
225 DPRINTK("usbif_disconnect attempted for non-existent usbif"
226 " (%u)\n", disconnect->domid);
227 disconnect->status = USBIF_BE_STATUS_INTERFACE_NOT_FOUND;
228 return 1; /* Caller will send response error message. */
231 if ( up->status == CONNECTED )
233 up->status = DISCONNECTING;
234 up->disconnect_rspid = rsp_id;
235 wmb(); /* Let other CPUs see the status change. */
236 free_irq(up->irq, up);
237 usbif_deschedule(up);
239 return 0; /* Caller should not send response message. */
242 disconnect->status = USBIF_BE_STATUS_OKAY;
246 void __init usbif_interface_init(void)
248 usbif_priv_cachep = kmem_cache_create("usbif_priv_cache",
249 sizeof(usbif_priv_t),
251 memset(usbif_priv_hash, 0, sizeof(usbif_priv_hash));