1/*
2 * Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li>
3 * Distributed under the terms of the MIT License.
4 *
5 */
6
7#include <KernelExport.h>
8#include <Drivers.h>
9#include <Errors.h>
10
11#include <USB.h>
12#include <malloc.h>
13#include <string.h> /* strerror */
14#include <stdlib.h> /* strtol */
15#include <stdio.h>  /* sprintf */
16
17
18#include "usb_vision.h"
19#include "tracing.h"
20
21#define BASENAME_LEN 0x10 /*must be synchronized with below !!!*/
22static const char *basename = "video/usb_vision/%u";
23
24status_t usb_vision_device_added(const usb_device *dev, void **cookie);
25status_t usb_vision_device_removed(void *cookie);
26
27static usb_notify_hooks notify_hooks = {
28    &usb_vision_device_added,
29    &usb_vision_device_removed
30};
31
32struct usb_module_info *usb;
33usb_vision_device *usb_vision_devices[DEVICES_COUNT];
34char * usb_vision_names[DEVICES_COUNT + 1];
35sem_id usb_vision_lock = -1;
36
37struct usb_support_descriptor supported_devices[] = {
38  {0, 0, 0, 0x0573, 0x4d31},
39};
40
41/* init_hardware - called once the first time the driver is loaded */
42status_t init_hardware (void){
43  TRACE("init_hardware\n"); /*special case - no file-logging activated now*/
44  return B_OK;
45}
46
47/* init_driver - optional function - called every time the driver is loaded. */
48status_t init_driver (void){
49  int i;
50  status_t status = B_OK;
51  load_setting();
52  create_log();
53
54  TRACE_FUNCALLS("init_driver\n");
55
56  if((status = get_module(B_USB_MODULE_NAME, (module_info **)&usb)) == B_OK){
57    if(usb){
58      for(i = 0; i < DEVICES_COUNT; i++)
59        usb_vision_devices[i] = 0;
60
61      usb_vision_names[0] = NULL;
62
63      (*usb->register_driver)(DRIVER_NAME, supported_devices, SIZEOF(supported_devices), DRIVER_NAME);
64      (*usb->install_notify)(DRIVER_NAME, &notify_hooks);
65
66      usb_vision_lock = create_sem(1, DRIVER_NAME"_devices_table_lock");
67    }else{
68      status = B_ERROR;
69      TRACE_ALWAYS("init_driver failed: usb:%08x", usb);
70    }
71  }else
72    TRACE_ALWAYS("init_driver failed:%lx cannot get a module %s", status, B_USB_MODULE_NAME);
73
74  TRACE_FUNCRET("init_driver returns:%08x\n", status);
75  return status;
76}
77
78
79/* uninit_driver - optional function - called every time the driver is unloaded */
80void uninit_driver (void){
81  int i;
82  TRACE_FUNCALLS("uninit_driver\n");
83
84  (*usb->uninstall_notify)(DRIVER_NAME);
85  acquire_sem(usb_vision_lock);
86
87  for(i = 0; i < DEVICES_COUNT; i++)
88    if(usb_vision_devices[i]){
89      free(usb_vision_devices[i]);
90      usb_vision_devices[i] = 0;
91    }
92
93  release_sem_etc(usb_vision_lock, 1, B_DO_NOT_RESCHEDULE);
94  delete_sem(usb_vision_lock);
95
96  for(i = 0; usb_vision_names[i]; i++)
97    free(usb_vision_names[i]);
98
99  put_module(B_USB_MODULE_NAME);
100}
101
102
103/* usb_vision_open - handle open() calls */
104
105static status_t usb_vision_open (const char *name, uint32 flags, void** cookie)
106{
107  int i;
108  status_t status = ENODEV;
109  TRACE_FUNCALLS("usb_vision_open:%s flags:%d cookie:%08x\n", name, flags, cookie);
110
111  for(i = 0; i < DEVICES_COUNT; i++)
112    TRACE("%08x\n", usb_vision_devices[i]);
113
114  *cookie = NULL;
115  i = strtol(name + BASENAME_LEN, NULL, 10);
116  if(i >= 0 && i < DEVICES_COUNT){
117    acquire_sem(usb_vision_lock);
118    if(usb_vision_devices[i]){
119      if(atomic_add(&usb_vision_devices[i]->open_count, 1) == 0){
120        *cookie = usb_vision_devices[i];
121        TRACE("cookie in open:%08x\n", *cookie);
122        status = B_OK;
123      }else{
124        atomic_add(&usb_vision_devices[i]->open_count, -1);
125        status = B_BUSY;
126      }
127    }
128    release_sem(usb_vision_lock);
129  }
130
131  TRACE_FUNCRET("usb_vision_open returns:%08x\n", status);
132  return status;
133}
134
135/* usb_vision_read - handle read() calls */
136
137static status_t usb_vision_read (void* cookie, off_t position, void *buf, size_t* num_bytes)
138{
139	*num_bytes = 0;				/* tell caller nothing was read */
140	return B_IO_ERROR;
141}
142
143
144/* usb_vision_write - handle write() calls */
145
146static status_t usb_vision_write (void* cookie, off_t position, const void* buffer, size_t* num_bytes)
147{
148	*num_bytes = 0;				/* tell caller nothing was written */
149	return B_IO_ERROR;
150}
151
152static status_t xet_nt_register(bool is_read, usb_vision_device *uvd, xet_nt100x_reg *ri)
153{
154  status_t status = B_ERROR;
155  //uint8 req_type = USB_REQTYPE_VENDOR | (is_read ? USB_REQTYPE_DEVICE_IN : USB_REQTYPE_DEVICE_OUT);
156
157  TRACE_FUNCALLS("set_nt_register:%08x, %08x\n", uvd, ri);
158  TRACE_FUNCRES(trace_reginfo, ri);
159
160  //(*usb->send_request)(uvd->dev, req_type,
161  //                     )
162
163  TRACE_FUNCRET("set_nt_register returns:%08x\n", status);
164  return status;
165}
166
167/* usb_vision_control - handle ioctl calls */
168static status_t usb_vision_control (void* cookie, uint32 op, void* arg, size_t len)
169{
170  status_t status = B_BAD_VALUE;
171  TRACE_FUNCALLS("usb_vision_control:%08x, %d, %08x, %d\n", cookie, op, arg, len);
172  switch(op){
173  case NT_IOCTL_READ_REGISTER:
174    status = xet_nt_register(true, (usb_vision_device *)cookie, (xet_nt100x_reg *) arg);
175    break;
176  case NT_IOCTL_WRITE_REGISTER:
177    status = xet_nt_register(false, (usb_vision_device *)cookie, (xet_nt100x_reg *) arg);
178    break;
179  default:
180    break;
181  }
182  TRACE_FUNCRET("usb_vision_control returns:%08x\n", status);
183  return status;
184}
185
186/* usb_vision_close - handle close() calls */
187static status_t usb_vision_close (void* cookie)
188{
189  status_t status = B_OK;//ENODEV;
190  TRACE_FUNCALLS("usb_vision_close:%08x\n", cookie);
191
192  TRACE_FUNCRET("usb_vision_close returns:%08x\n", status);
193  return status;
194}
195
196
197/* usb_vision_free - called after the last device is closed, and after all i/o is complete. */
198static status_t usb_vision_free (void* cookie)
199{
200  status_t status = B_OK;
201  TRACE_FUNCALLS("usb_vision_free:%08x\n", cookie);
202
203  if(cookie){
204    usb_vision_device *uvd = (usb_vision_device *)cookie;
205    atomic_add(&uvd->open_count, -1);
206  }
207
208  TRACE_FUNCRET("usb_vision_free returns:%08x\n", status);
209  return status;
210}
211
212/* function pointers for the device hooks entry points */
213device_hooks usb_vision_hooks = {
214	usb_vision_open, 			/* -> open entry point */
215	usb_vision_close, 			/* -> close entry point */
216	usb_vision_free,			/* -> free cookie */
217	usb_vision_control, 		/* -> control entry point */
218	usb_vision_read,			/* -> read entry point */
219	usb_vision_write			/* -> write entry point */
220};
221
222/* publish_devices - return a null-terminated array of devices
223   supported by this driver. */
224
225const char** publish_devices(){
226  int i, j;
227  TRACE_FUNCALLS("publish_devices\n");
228
229  for(i=0; usb_vision_names[i]; i++)
230    free(usb_vision_names[i]);
231
232  acquire_sem(usb_vision_lock);
233  for(i=0, j=0; i < DEVICES_COUNT; i++){
234    if(usb_vision_devices[i]){
235      usb_vision_names[j] = malloc(strlen(basename + 2));
236      if(usb_vision_names[j]){
237        sprintf(usb_vision_names[j], basename, i);
238        j++;
239      }
240      else
241        TRACE_ALWAYS("publish_devices - NO MEMORY\n");
242    }
243  }
244  usb_vision_names[j] = NULL;
245  release_sem(usb_vision_lock);
246
247  return (const char **)&usb_vision_names[0];
248}
249
250/* find_device - return ptr to device hooks structure for a
251   given device name */
252device_hooks* find_device(const char* name){
253  TRACE_FUNCALLS("find_device(%s)\n", name);
254  return &usb_vision_hooks;
255}
256
257static status_t create_add_device(usb_vision_device *uvd, const struct usb_configuration_info *uci,
258                                              struct usb_endpoint_info *control_epi,
259                                              struct usb_endpoint_info *data_epi){
260//  char name[32];
261  status_t status = ENODEV;
262//  size_t buf_len;
263  int i = 0;
264
265  TRACE_FUNCALLS("create_add_device(%08x, %08x, %08x, %08x)\n", uvd, uci, control_epi, data_epi);
266
267  acquire_sem(usb_vision_lock);
268
269  for(i = 0; i < DEVICES_COUNT; i++){
270    if(usb_vision_devices[i] != NULL)
271      continue;
272
273    usb_vision_devices[i] = uvd;
274   /*
275    usd->active = 1;
276    usd->open = 0;
277
278    sprintf(name, "usb_vision:%d:done_read", i );
279    usd->done_read = create_sem(0, name);
280
281    sprintf(name, "usb_vision:%d:done_write", i);
282    usd->done_write = create_sem(0, name);
283
284    usd->tty = NULL;
285
286    buf_len = usd->read_buffer_size + usd->write_buffer_size + usd->interrupt_buffer_size;
287
288    usd->buffers_area = create_area("usb_serial:buffers_area", (void *)&usd->read_buffer, B_ANY_KERNEL_ADDRESS,
289                                        ROUNDUP(buf_len, B_PAGE_SIZE),
290                                           B_CONTIGUOUS, B_READ_AREA|B_WRITE_AREA);
291
292    usd->write_buffer     = usd->read_buffer + usd->read_buffer_size;
293    usd->interrupt_buffer = usd->write_buffer + usd->write_buffer_size;
294*/
295    (*usb->set_configuration)(uvd->dev, uci);
296
297    uvd->control_pipe = control_epi->handle;
298    uvd->data_pipe = data_epi->handle;
299
300    status = B_OK;
301    break;
302  }
303  release_sem(usb_vision_lock);
304
305  TRACE_FUNCRET("add_device returns:%08x\n", status);
306  return status;
307}
308
309static status_t add_device(usb_vision_device *uvd, const usb_configuration_info *uci){
310  usb_endpoint_info *control_epi = NULL;
311  usb_endpoint_info *data_epi = NULL;
312  status_t status = ENODEV;
313  int i = 0;
314  usb_interface_info *uii = uci->interface[0].active;
315  TRACE_FUNCALLS("> add_device(%08x, %08x)\n", uvd, uci);
316
317  for(i=0; i < uii->endpoint_count; i++){
318    if(uii->endpoint[i].descr->attributes == USB_EP_ATTR_ISOCHRONOUS){
319      if((uii->endpoint[i].descr->endpoint_address & USB_EP_ADDR_DIR_IN) == USB_EP_ADDR_DIR_IN){
320        data_epi = &uii->endpoint[i];
321        TRACE("iso_ep:%d\n", i);
322      }
323    }
324    if(uii->endpoint[i].descr->attributes == USB_EP_ATTR_CONTROL){
325      control_epi = &uii->endpoint[i];
326        TRACE("cont_ep:%d\n", i);
327    }
328    if(control_epi && data_epi)
329      break;
330  }
331
332  if(control_epi && data_epi){
333    status = create_add_device(uvd, uci, control_epi, data_epi);
334  }
335
336  TRACE_FUNCRET("< create_add_device returns:%08x\n", status);
337  return status;
338}
339
340status_t usb_vision_device_added(const usb_device *dev, void **cookie){
341  int dev_idx;
342  status_t status = B_OK;
343  const usb_device_descriptor *udd;
344  usb_vision_device *uvd = 0;
345  TRACE_FUNCALLS("usb_vision_device_added:%08x cookie:%08x\n", dev, cookie);
346
347  udd = (*usb->get_device_descriptor)(dev);
348  TRACE_ALWAYS("Probing device: %08x/%08x\n", udd->vendor_id, udd->product_id);
349
350  *cookie = 0;
351  for(dev_idx = 0; dev_idx < SIZEOF(supported_devices); dev_idx++)
352    if(supported_devices[dev_idx].vendor == udd->vendor_id
353      && supported_devices[dev_idx].product == udd->product_id){
354      const usb_configuration_info *uci;
355      int cfg_idx;
356      for(cfg_idx = 0; (uci = (*usb->get_nth_configuration)(dev, cfg_idx)) != NULL; cfg_idx++){
357        uvd = malloc(sizeof(usb_vision_device));
358        if(uvd){
359          memset(uvd, 0, sizeof(usb_vision_device));
360          uvd->dev = dev;
361          if((status = add_device(uvd, uci)) == B_OK){
362            *cookie = (void *)dev;
363            TRACE_ALWAYS("(%04x/%04x) added \n", supported_devices[dev_idx].vendor,
364                                               supported_devices[dev_idx].product);
365            break;
366          }else
367            free(uvd);
368        }else
369          status = B_NO_MEMORY;
370      }
371      break;
372    }
373
374  TRACE_FUNCRET("usb_vision_device_added returns:%08x\n", status);
375  return status;
376}
377
378status_t usb_vision_device_removed(void *cookie){
379  int i;
380  status_t status = B_OK;
381  struct usb_device *ud = (struct usb_device *) cookie;
382  TRACE_FUNCALLS("usb_vision_device_removed:%08x\n", cookie);
383
384  acquire_sem(usb_vision_lock);
385
386  for(i = 0; i < DEVICES_COUNT; i++ ){
387    usb_vision_device *uvd = usb_vision_devices[i];
388    if(uvd){
389      if(ud == uvd->dev){
390        free(uvd);
391        usb_vision_devices[i] = 0;
392      }
393    }
394  }
395
396  release_sem(usb_vision_lock);
397
398  TRACE_FUNCRET("usb_vision_device_removed returns:%08x\n", status);
399  return status;
400}
401