| /* |
| * cpia_usb CPiA USB driver |
| * |
| * Supports CPiA based parallel port Video Camera's. |
| * |
| * Copyright (C) 1999 Jochen Scharrlach <Jochen.Scharrlach@schwaben.de> |
| * Copyright (C) 1999, 2000 Johannes Erdfelt <johannes@erdfelt.com> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| /* define _CPIA_DEBUG_ for verbose debug output (see cpia.h) */ |
| /* #define _CPIA_DEBUG_ 1 */ |
| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/wait.h> |
| #include <linux/list.h> |
| #include <linux/slab.h> |
| #include <linux/vmalloc.h> |
| #include <linux/usb.h> |
| |
| #include "cpia.h" |
| |
| #define USB_REQ_CPIA_GRAB_FRAME 0xC1 |
| #define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 |
| #define WAIT_FOR_NEXT_FRAME 0 |
| #define FORCE_FRAME_UPLOAD 1 |
| |
| #define FRAMES_PER_DESC 10 |
| #define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ |
| #define CPIA_NUMSBUF 2 |
| #define STREAM_BUF_SIZE (PAGE_SIZE * 4) |
| #define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) |
| |
| struct cpia_sbuf { |
| char *data; |
| struct urb *urb; |
| }; |
| |
| #define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100) |
| enum framebuf_status { |
| FRAME_EMPTY, |
| FRAME_READING, |
| FRAME_READY, |
| FRAME_ERROR, |
| }; |
| |
| struct framebuf { |
| int length; |
| enum framebuf_status status; |
| u8 data[FRAMEBUF_LEN]; |
| struct framebuf *next; |
| }; |
| |
| struct usb_cpia { |
| /* Device structure */ |
| struct usb_device *dev; |
| |
| unsigned char iface; |
| wait_queue_head_t wq_stream; |
| |
| int cursbuf; /* Current receiving sbuf */ |
| struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ |
| |
| int streaming; |
| int open; |
| int present; |
| struct framebuf *buffers[3]; |
| struct framebuf *curbuff, *workbuff; |
| }; |
| |
| static int cpia_usb_open(void *privdata); |
| static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), |
| void *cbdata); |
| static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data); |
| static int cpia_usb_streamStart(void *privdata); |
| static int cpia_usb_streamStop(void *privdata); |
| static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock); |
| static int cpia_usb_close(void *privdata); |
| |
| #define ABOUT "USB driver for Vision CPiA based cameras" |
| |
| static struct cpia_camera_ops cpia_usb_ops = { |
| cpia_usb_open, |
| cpia_usb_registerCallback, |
| cpia_usb_transferCmd, |
| cpia_usb_streamStart, |
| cpia_usb_streamStop, |
| cpia_usb_streamRead, |
| cpia_usb_close, |
| 0, |
| THIS_MODULE |
| }; |
| |
| static LIST_HEAD(cam_list); |
| static spinlock_t cam_list_lock_usb; |
| |
| static void cpia_usb_complete(struct urb *urb, struct pt_regs *regs) |
| { |
| int i; |
| char *cdata; |
| struct usb_cpia *ucpia; |
| |
| if (!urb || !urb->context) |
| return; |
| |
| ucpia = (struct usb_cpia *) urb->context; |
| |
| if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open) |
| return; |
| |
| if (ucpia->workbuff->status == FRAME_EMPTY) { |
| ucpia->workbuff->status = FRAME_READING; |
| ucpia->workbuff->length = 0; |
| } |
| |
| for (i = 0; i < urb->number_of_packets; i++) { |
| int n = urb->iso_frame_desc[i].actual_length; |
| int st = urb->iso_frame_desc[i].status; |
| |
| cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; |
| |
| if (st) |
| printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st); |
| |
| if (FRAMEBUF_LEN < ucpia->workbuff->length + n) { |
| printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n); |
| return; |
| } |
| |
| if (n) { |
| if ((ucpia->workbuff->length > 0) || |
| (0x19 == cdata[0] && 0x68 == cdata[1])) { |
| memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n); |
| ucpia->workbuff->length += n; |
| } else |
| DBG("Ignoring packet!\n"); |
| } else { |
| if (ucpia->workbuff->length > 4 && |
| 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] && |
| 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] && |
| 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] && |
| 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) { |
| ucpia->workbuff->status = FRAME_READY; |
| ucpia->curbuff = ucpia->workbuff; |
| ucpia->workbuff = ucpia->workbuff->next; |
| ucpia->workbuff->status = FRAME_EMPTY; |
| ucpia->workbuff->length = 0; |
| |
| if (waitqueue_active(&ucpia->wq_stream)) |
| wake_up_interruptible(&ucpia->wq_stream); |
| } |
| } |
| } |
| |
| /* resubmit */ |
| urb->dev = ucpia->dev; |
| if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0) |
| printk(KERN_ERR "%s: usb_submit_urb ret %d\n", __FUNCTION__, i); |
| } |
| |
| static int cpia_usb_open(void *privdata) |
| { |
| struct usb_cpia *ucpia = (struct usb_cpia *) privdata; |
| struct urb *urb; |
| int ret, retval = 0, fx, err; |
| |
| if (!ucpia) |
| return -EINVAL; |
| |
| ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); |
| if (!ucpia->sbuf[0].data) |
| return -EINVAL; |
| |
| ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); |
| if (!ucpia->sbuf[1].data) { |
| retval = -EINVAL; |
| goto error_0; |
| } |
| |
| ret = usb_set_interface(ucpia->dev, ucpia->iface, 3); |
| if (ret < 0) { |
| printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret); |
| retval = -EBUSY; |
| goto error_1; |
| } |
| |
| ucpia->buffers[0]->status = FRAME_EMPTY; |
| ucpia->buffers[0]->length = 0; |
| ucpia->buffers[1]->status = FRAME_EMPTY; |
| ucpia->buffers[1]->length = 0; |
| ucpia->buffers[2]->status = FRAME_EMPTY; |
| ucpia->buffers[2]->length = 0; |
| ucpia->curbuff = ucpia->buffers[0]; |
| ucpia->workbuff = ucpia->buffers[1]; |
| |
| /* We double buffer the Iso lists, and also know the polling |
| * interval is every frame (1 == (1 << (bInterval -1))). |
| */ |
| urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); |
| if (!urb) { |
| printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); |
| retval = -ENOMEM; |
| goto error_1; |
| } |
| |
| ucpia->sbuf[0].urb = urb; |
| urb->dev = ucpia->dev; |
| urb->context = ucpia; |
| urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); |
| urb->transfer_flags = URB_ISO_ASAP; |
| urb->transfer_buffer = ucpia->sbuf[0].data; |
| urb->complete = cpia_usb_complete; |
| urb->number_of_packets = FRAMES_PER_DESC; |
| urb->interval = 1; |
| urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; |
| for (fx = 0; fx < FRAMES_PER_DESC; fx++) { |
| urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; |
| urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; |
| } |
| |
| urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); |
| if (!urb) { |
| printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 1\n"); |
| retval = -ENOMEM; |
| goto error_urb0; |
| } |
| |
| ucpia->sbuf[1].urb = urb; |
| urb->dev = ucpia->dev; |
| urb->context = ucpia; |
| urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); |
| urb->transfer_flags = URB_ISO_ASAP; |
| urb->transfer_buffer = ucpia->sbuf[1].data; |
| urb->complete = cpia_usb_complete; |
| urb->number_of_packets = FRAMES_PER_DESC; |
| urb->interval = 1; |
| urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; |
| for (fx = 0; fx < FRAMES_PER_DESC; fx++) { |
| urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; |
| urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; |
| } |
| |
| /* queue the ISO urbs, and resubmit in the completion handler */ |
| err = usb_submit_urb(ucpia->sbuf[0].urb, GFP_KERNEL); |
| if (err) { |
| printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", |
| err); |
| goto error_urb1; |
| } |
| err = usb_submit_urb(ucpia->sbuf[1].urb, GFP_KERNEL); |
| if (err) { |
| printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", |
| err); |
| goto error_urb1; |
| } |
| |
| ucpia->streaming = 1; |
| ucpia->open = 1; |
| |
| return 0; |
| |
| error_urb1: /* free urb 1 */ |
| usb_free_urb(ucpia->sbuf[1].urb); |
| ucpia->sbuf[1].urb = NULL; |
| error_urb0: /* free urb 0 */ |
| usb_free_urb(ucpia->sbuf[0].urb); |
| ucpia->sbuf[0].urb = NULL; |
| error_1: |
| kfree (ucpia->sbuf[1].data); |
| ucpia->sbuf[1].data = NULL; |
| error_0: |
| kfree (ucpia->sbuf[0].data); |
| ucpia->sbuf[0].data = NULL; |
| |
| return retval; |
| } |
| |
| // |
| // convenience functions |
| // |
| |
| /**************************************************************************** |
| * |
| * WritePacket |
| * |
| ***************************************************************************/ |
| static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size) |
| { |
| if (!packet) |
| return -EINVAL; |
| |
| return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), |
| packet[1] + (packet[0] << 8), |
| USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
| packet[2] + (packet[3] << 8), |
| packet[4] + (packet[5] << 8), buf, size, 1000); |
| } |
| |
| /**************************************************************************** |
| * |
| * ReadPacket |
| * |
| ***************************************************************************/ |
| static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size) |
| { |
| if (!packet || size <= 0) |
| return -EINVAL; |
| |
| return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), |
| packet[1] + (packet[0] << 8), |
| USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
| packet[2] + (packet[3] << 8), |
| packet[4] + (packet[5] << 8), buf, size, 1000); |
| } |
| |
| static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data) |
| { |
| int err = 0; |
| int databytes; |
| struct usb_cpia *ucpia = (struct usb_cpia *)privdata; |
| struct usb_device *udev = ucpia->dev; |
| |
| if (!udev) { |
| DBG("Internal driver error: udev is NULL\n"); |
| return -EINVAL; |
| } |
| |
| if (!command) { |
| DBG("Internal driver error: command is NULL\n"); |
| return -EINVAL; |
| } |
| |
| databytes = (((int)command[7])<<8) | command[6]; |
| |
| if (command[0] == DATA_IN) { |
| u8 buffer[8]; |
| |
| if (!data) { |
| DBG("Internal driver error: data is NULL\n"); |
| return -EINVAL; |
| } |
| |
| err = ReadPacket(udev, command, buffer, 8); |
| if (err < 0) |
| return err; |
| |
| memcpy(data, buffer, databytes); |
| } else if(command[0] == DATA_OUT) |
| WritePacket(udev, command, data, databytes); |
| else { |
| DBG("Unexpected first byte of command: %x\n", command[0]); |
| err = -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), |
| void *cbdata) |
| { |
| return -ENODEV; |
| } |
| |
| static int cpia_usb_streamStart(void *privdata) |
| { |
| return -ENODEV; |
| } |
| |
| static int cpia_usb_streamStop(void *privdata) |
| { |
| return -ENODEV; |
| } |
| |
| static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock) |
| { |
| struct usb_cpia *ucpia = (struct usb_cpia *) privdata; |
| struct framebuf *mybuff; |
| |
| if (!ucpia || !ucpia->present) |
| return -1; |
| |
| if (ucpia->curbuff->status != FRAME_READY) |
| interruptible_sleep_on(&ucpia->wq_stream); |
| else |
| DBG("Frame already waiting!\n"); |
| |
| mybuff = ucpia->curbuff; |
| |
| if (!mybuff) |
| return -1; |
| |
| if (mybuff->status != FRAME_READY || mybuff->length < 4) { |
| DBG("Something went wrong!\n"); |
| return -1; |
| } |
| |
| memcpy(frame, mybuff->data, mybuff->length); |
| mybuff->status = FRAME_EMPTY; |
| |
| /* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */ |
| /* mybuff->length, frame[0], frame[1], */ |
| /* frame[mybuff->length-4], frame[mybuff->length-3], */ |
| /* frame[mybuff->length-2], frame[mybuff->length-1]); */ |
| |
| return mybuff->length; |
| } |
| |
| static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try) |
| { |
| if (!ucpia->streaming) |
| return; |
| |
| ucpia->streaming = 0; |
| |
| /* Set packet size to 0 */ |
| if (try) { |
| int ret; |
| |
| ret = usb_set_interface(ucpia->dev, ucpia->iface, 0); |
| if (ret < 0) { |
| printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret); |
| return; |
| } |
| } |
| |
| /* Unschedule all of the iso td's */ |
| if (ucpia->sbuf[1].urb) { |
| usb_kill_urb(ucpia->sbuf[1].urb); |
| usb_free_urb(ucpia->sbuf[1].urb); |
| ucpia->sbuf[1].urb = NULL; |
| } |
| |
| kfree(ucpia->sbuf[1].data); |
| ucpia->sbuf[1].data = NULL; |
| |
| if (ucpia->sbuf[0].urb) { |
| usb_kill_urb(ucpia->sbuf[0].urb); |
| usb_free_urb(ucpia->sbuf[0].urb); |
| ucpia->sbuf[0].urb = NULL; |
| } |
| |
| kfree(ucpia->sbuf[0].data); |
| ucpia->sbuf[0].data = NULL; |
| } |
| |
| static int cpia_usb_close(void *privdata) |
| { |
| struct usb_cpia *ucpia = (struct usb_cpia *) privdata; |
| |
| if(!ucpia) |
| return -ENODEV; |
| |
| ucpia->open = 0; |
| |
| /* ucpia->present = 0 protects against trying to reset the |
| * alt setting if camera is physically disconnected while open */ |
| cpia_usb_free_resources(ucpia, ucpia->present); |
| |
| return 0; |
| } |
| |
| /* Probing and initializing */ |
| |
| static int cpia_probe(struct usb_interface *intf, |
| const struct usb_device_id *id) |
| { |
| struct usb_device *udev = interface_to_usbdev(intf); |
| struct usb_host_interface *interface; |
| struct usb_cpia *ucpia; |
| struct cam_data *cam; |
| int ret; |
| |
| /* A multi-config CPiA camera? */ |
| if (udev->descriptor.bNumConfigurations != 1) |
| return -ENODEV; |
| |
| interface = intf->cur_altsetting; |
| |
| printk(KERN_INFO "USB CPiA camera found\n"); |
| |
| ucpia = kzalloc(sizeof(*ucpia), GFP_KERNEL); |
| if (!ucpia) { |
| printk(KERN_ERR "couldn't kmalloc cpia struct\n"); |
| return -ENOMEM; |
| } |
| |
| ucpia->dev = udev; |
| ucpia->iface = interface->desc.bInterfaceNumber; |
| init_waitqueue_head(&ucpia->wq_stream); |
| |
| ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0])); |
| if (!ucpia->buffers[0]) { |
| printk(KERN_ERR "couldn't vmalloc frame buffer 0\n"); |
| goto fail_alloc_0; |
| } |
| |
| ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1])); |
| if (!ucpia->buffers[1]) { |
| printk(KERN_ERR "couldn't vmalloc frame buffer 1\n"); |
| goto fail_alloc_1; |
| } |
| |
| ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2])); |
| if (!ucpia->buffers[2]) { |
| printk(KERN_ERR "couldn't vmalloc frame buffer 2\n"); |
| goto fail_alloc_2; |
| } |
| |
| ucpia->buffers[0]->next = ucpia->buffers[1]; |
| ucpia->buffers[1]->next = ucpia->buffers[2]; |
| ucpia->buffers[2]->next = ucpia->buffers[0]; |
| |
| ret = usb_set_interface(udev, ucpia->iface, 0); |
| if (ret < 0) { |
| printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret); |
| /* goto fail_all; */ |
| } |
| |
| /* Before register_camera, important */ |
| ucpia->present = 1; |
| |
| cam = cpia_register_camera(&cpia_usb_ops, ucpia); |
| if (!cam) { |
| LOG("failed to cpia_register_camera\n"); |
| goto fail_all; |
| } |
| |
| spin_lock( &cam_list_lock_usb ); |
| list_add( &cam->cam_data_list, &cam_list ); |
| spin_unlock( &cam_list_lock_usb ); |
| |
| usb_set_intfdata(intf, cam); |
| return 0; |
| |
| fail_all: |
| vfree(ucpia->buffers[2]); |
| ucpia->buffers[2] = NULL; |
| fail_alloc_2: |
| vfree(ucpia->buffers[1]); |
| ucpia->buffers[1] = NULL; |
| fail_alloc_1: |
| vfree(ucpia->buffers[0]); |
| ucpia->buffers[0] = NULL; |
| fail_alloc_0: |
| kfree(ucpia); |
| return -EIO; |
| } |
| |
| static void cpia_disconnect(struct usb_interface *intf); |
| |
| static struct usb_device_id cpia_id_table [] = { |
| { USB_DEVICE(0x0553, 0x0002) }, |
| { USB_DEVICE(0x0813, 0x0001) }, |
| { } /* Terminating entry */ |
| }; |
| |
| MODULE_DEVICE_TABLE (usb, cpia_id_table); |
| MODULE_LICENSE("GPL"); |
| |
| |
| static struct usb_driver cpia_driver = { |
| .name = "cpia", |
| .probe = cpia_probe, |
| .disconnect = cpia_disconnect, |
| .id_table = cpia_id_table, |
| }; |
| |
| static void cpia_disconnect(struct usb_interface *intf) |
| { |
| struct cam_data *cam = usb_get_intfdata(intf); |
| struct usb_cpia *ucpia; |
| struct usb_device *udev; |
| |
| usb_set_intfdata(intf, NULL); |
| if (!cam) |
| return; |
| |
| ucpia = (struct usb_cpia *) cam->lowlevel_data; |
| spin_lock( &cam_list_lock_usb ); |
| list_del(&cam->cam_data_list); |
| spin_unlock( &cam_list_lock_usb ); |
| |
| ucpia->present = 0; |
| |
| cpia_unregister_camera(cam); |
| if(ucpia->open) |
| cpia_usb_close(cam->lowlevel_data); |
| |
| ucpia->curbuff->status = FRAME_ERROR; |
| |
| if (waitqueue_active(&ucpia->wq_stream)) |
| wake_up_interruptible(&ucpia->wq_stream); |
| |
| udev = interface_to_usbdev(intf); |
| |
| ucpia->curbuff = ucpia->workbuff = NULL; |
| |
| vfree(ucpia->buffers[2]); |
| ucpia->buffers[2] = NULL; |
| |
| vfree(ucpia->buffers[1]); |
| ucpia->buffers[1] = NULL; |
| |
| vfree(ucpia->buffers[0]); |
| ucpia->buffers[0] = NULL; |
| |
| cam->lowlevel_data = NULL; |
| kfree(ucpia); |
| } |
| |
| static int __init usb_cpia_init(void) |
| { |
| printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, |
| CPIA_USB_MAJ_VER,CPIA_USB_MIN_VER,CPIA_USB_PATCH_VER); |
| |
| spin_lock_init(&cam_list_lock_usb); |
| return usb_register(&cpia_driver); |
| } |
| |
| static void __exit usb_cpia_cleanup(void) |
| { |
| usb_deregister(&cpia_driver); |
| } |
| |
| |
| module_init (usb_cpia_init); |
| module_exit (usb_cpia_cleanup); |
| |