diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/gspca/Makefile | 3 | ||||
-rw-r--r-- | drivers/media/video/gspca/gspca.c | 121 | ||||
-rw-r--r-- | drivers/media/video/gspca/gspca.h | 17 | ||||
-rw-r--r-- | drivers/media/video/gspca/pac207.c | 939 |
4 files changed, 1018 insertions, 62 deletions
diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 885d4454d8e5..81170c19ba47 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | obj-$(CONFIG_GSPCA) += gspca_main.o gspca_stk014.o | 1 | obj-$(CONFIG_GSPCA) += gspca_main.o gspca_pac207.o gspca_stk014.o |
2 | 2 | ||
3 | gspca_main-objs := gspca.o | 3 | gspca_main-objs := gspca.o |
4 | gspca_pac207-objs := pac207.o | ||
4 | gspca_stk014-objs := stk014.o | 5 | gspca_stk014-objs := stk014.o |
diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 4fe082ff7074..c4735e133611 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c | |||
@@ -39,8 +39,8 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); | |||
39 | MODULE_DESCRIPTION("GSPCA USB Camera Driver"); | 39 | MODULE_DESCRIPTION("GSPCA USB Camera Driver"); |
40 | MODULE_LICENSE("GPL"); | 40 | MODULE_LICENSE("GPL"); |
41 | 41 | ||
42 | #define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 0, 26) | 42 | #define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 0, 30) |
43 | static const char version[] = "0.0.26"; | 43 | static const char version[] = "0.0.30"; |
44 | 44 | ||
45 | static int video_nr = -1; | 45 | static int video_nr = -1; |
46 | 46 | ||
@@ -172,8 +172,7 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, | |||
172 | { | 172 | { |
173 | int i, j; | 173 | int i, j; |
174 | 174 | ||
175 | PDEBUG(D_PACK, "add t:%d l:%d %02x %02x %02x %02x...", | 175 | PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len); |
176 | packet_type, len, data[0], data[1], data[2], data[3]); | ||
177 | 176 | ||
178 | /* when start of a new frame, if the current frame buffer | 177 | /* when start of a new frame, if the current frame buffer |
179 | * is not queued, discard the whole frame */ | 178 | * is not queued, discard the whole frame */ |
@@ -346,7 +345,6 @@ static int gspca_kill_transfer(struct gspca_dev *gspca_dev) | |||
346 | unsigned int i; | 345 | unsigned int i; |
347 | 346 | ||
348 | PDEBUG(D_STREAM, "kill transfer"); | 347 | PDEBUG(D_STREAM, "kill transfer"); |
349 | gspca_dev->streaming = 0; | ||
350 | for (i = 0; i < NURBS; ++i) { | 348 | for (i = 0; i < NURBS; ++i) { |
351 | urb = gspca_dev->pktbuf[i].urb; | 349 | urb = gspca_dev->pktbuf[i].urb; |
352 | if (urb == NULL) | 350 | if (urb == NULL) |
@@ -501,9 +499,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) | |||
501 | struct usb_host_endpoint *ep; | 499 | struct usb_host_endpoint *ep; |
502 | int n, ret; | 500 | int n, ret; |
503 | 501 | ||
504 | ret = mutex_lock_interruptible(&gspca_dev->usb_lock); | 502 | if (mutex_lock_interruptible(&gspca_dev->usb_lock)) |
505 | if (ret < 0) | 503 | return -ERESTARTSYS; |
506 | return ret; | ||
507 | 504 | ||
508 | /* set the max alternate setting and loop until urb submit succeeds */ | 505 | /* set the max alternate setting and loop until urb submit succeeds */ |
509 | intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); | 506 | intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); |
@@ -531,6 +528,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) | |||
531 | if (ret < 0) { | 528 | if (ret < 0) { |
532 | PDEBUG(D_ERR|D_STREAM, | 529 | PDEBUG(D_ERR|D_STREAM, |
533 | "usb_submit_urb [%d] err %d", n, ret); | 530 | "usb_submit_urb [%d] err %d", n, ret); |
531 | gspca_dev->streaming = 0; | ||
534 | gspca_kill_transfer(gspca_dev); | 532 | gspca_kill_transfer(gspca_dev); |
535 | if (ret == -ENOSPC) | 533 | if (ret == -ENOSPC) |
536 | break; /* try the previous alt */ | 534 | break; /* try the previous alt */ |
@@ -555,9 +553,9 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) | |||
555 | return ret; | 553 | return ret; |
556 | } | 554 | } |
557 | 555 | ||
556 | /* Note both the queue and the usb lock should be hold when calling this */ | ||
558 | static void gspca_stream_off(struct gspca_dev *gspca_dev) | 557 | static void gspca_stream_off(struct gspca_dev *gspca_dev) |
559 | { | 558 | { |
560 | mutex_lock_interruptible(&gspca_dev->usb_lock); | ||
561 | gspca_dev->streaming = 0; | 559 | gspca_dev->streaming = 0; |
562 | if (gspca_dev->present) { | 560 | if (gspca_dev->present) { |
563 | gspca_dev->sd_desc->stopN(gspca_dev); | 561 | gspca_dev->sd_desc->stopN(gspca_dev); |
@@ -571,7 +569,6 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev) | |||
571 | wake_up_interruptible(&gspca_dev->wq); | 569 | wake_up_interruptible(&gspca_dev->wq); |
572 | PDEBUG(D_ERR|D_STREAM, "stream off no device ??"); | 570 | PDEBUG(D_ERR|D_STREAM, "stream off no device ??"); |
573 | } | 571 | } |
574 | mutex_unlock(&gspca_dev->usb_lock); | ||
575 | } | 572 | } |
576 | 573 | ||
577 | static int gspca_set_default_mode(struct gspca_dev *gspca_dev) | 574 | static int gspca_set_default_mode(struct gspca_dev *gspca_dev) |
@@ -791,9 +788,7 @@ static int vidioc_try_fmt_cap(struct file *file, | |||
791 | { | 788 | { |
792 | int ret; | 789 | int ret; |
793 | 790 | ||
794 | /* mutex_lock_interruptible(&gspca_dev->queue_lock); */ | ||
795 | ret = try_fmt_cap(file, priv, fmt); | 791 | ret = try_fmt_cap(file, priv, fmt); |
796 | /* mutex_unlock(&gspca_dev->queue_lock); */ | ||
797 | if (ret < 0) | 792 | if (ret < 0) |
798 | return ret; | 793 | return ret; |
799 | return 0; | 794 | return 0; |
@@ -812,7 +807,8 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, | |||
812 | fmt->fmt.pix.width, fmt->fmt.pix.height); | 807 | fmt->fmt.pix.width, fmt->fmt.pix.height); |
813 | } | 808 | } |
814 | #endif | 809 | #endif |
815 | mutex_lock_interruptible(&gspca_dev->queue_lock); | 810 | if (mutex_lock_interruptible(&gspca_dev->queue_lock)) |
811 | return -ERESTARTSYS; | ||
816 | ret = try_fmt_cap(file, priv, fmt); | 812 | ret = try_fmt_cap(file, priv, fmt); |
817 | if (ret < 0) | 813 | if (ret < 0) |
818 | goto out; | 814 | goto out; |
@@ -820,8 +816,14 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv, | |||
820 | if (ret == gspca_dev->curr_mode) | 816 | if (ret == gspca_dev->curr_mode) |
821 | goto out; /* same mode */ | 817 | goto out; /* same mode */ |
822 | was_streaming = gspca_dev->streaming; | 818 | was_streaming = gspca_dev->streaming; |
823 | if (was_streaming != 0) | 819 | if (was_streaming != 0) { |
820 | if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { | ||
821 | ret = -ERESTARTSYS; | ||
822 | goto out; | ||
823 | } | ||
824 | gspca_stream_off(gspca_dev); | 824 | gspca_stream_off(gspca_dev); |
825 | mutex_unlock(&gspca_dev->usb_lock); | ||
826 | } | ||
825 | gspca_dev->width = (int) fmt->fmt.pix.width; | 827 | gspca_dev->width = (int) fmt->fmt.pix.width; |
826 | gspca_dev->height = (int) fmt->fmt.pix.height; | 828 | gspca_dev->height = (int) fmt->fmt.pix.height; |
827 | gspca_dev->pixfmt = fmt->fmt.pix.pixelformat; | 829 | gspca_dev->pixfmt = fmt->fmt.pix.pixelformat; |
@@ -840,9 +842,8 @@ static int dev_open(struct inode *inode, struct file *file) | |||
840 | 842 | ||
841 | PDEBUG(D_STREAM, "opening"); | 843 | PDEBUG(D_STREAM, "opening"); |
842 | gspca_dev = (struct gspca_dev *) video_devdata(file); | 844 | gspca_dev = (struct gspca_dev *) video_devdata(file); |
843 | ret = mutex_lock_interruptible(&gspca_dev->queue_lock); | 845 | if (mutex_lock_interruptible(&gspca_dev->queue_lock)) |
844 | if (ret < 0) | 846 | return -ERESTARTSYS; |
845 | return ret; | ||
846 | if (!gspca_dev->present) { | 847 | if (!gspca_dev->present) { |
847 | ret = -ENODEV; | 848 | ret = -ENODEV; |
848 | goto out; | 849 | goto out; |
@@ -850,16 +851,17 @@ static int dev_open(struct inode *inode, struct file *file) | |||
850 | 851 | ||
851 | /* if not done yet, initialize the sensor */ | 852 | /* if not done yet, initialize the sensor */ |
852 | if (gspca_dev->users == 0) { | 853 | if (gspca_dev->users == 0) { |
853 | ret = mutex_lock_interruptible(&gspca_dev->usb_lock); | 854 | if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { |
854 | if (ret < 0) | 855 | ret = -ERESTARTSYS; |
855 | goto out; | 856 | goto out; |
857 | } | ||
856 | ret = gspca_dev->sd_desc->open(gspca_dev); | 858 | ret = gspca_dev->sd_desc->open(gspca_dev); |
857 | mutex_unlock(&gspca_dev->usb_lock); | 859 | mutex_unlock(&gspca_dev->usb_lock); |
858 | if (ret != 0) { | 860 | if (ret != 0) { |
859 | PDEBUG(D_ERR|D_CONF, "init device failed %d", ret); | 861 | PDEBUG(D_ERR|D_CONF, "init device failed %d", ret); |
860 | goto out; | 862 | goto out; |
861 | } | 863 | } |
862 | } else if (gspca_dev->users > 8) { /* (arbitrary value) */ | 864 | } else if (gspca_dev->users > 4) { /* (arbitrary value) */ |
863 | ret = -EBUSY; | 865 | ret = -EBUSY; |
864 | goto out; | 866 | goto out; |
865 | } | 867 | } |
@@ -886,21 +888,20 @@ static int dev_close(struct inode *inode, struct file *file) | |||
886 | struct gspca_dev *gspca_dev = file->private_data; | 888 | struct gspca_dev *gspca_dev = file->private_data; |
887 | 889 | ||
888 | PDEBUG(D_STREAM, "closing"); | 890 | PDEBUG(D_STREAM, "closing"); |
889 | if (gspca_dev->streaming) { | 891 | if (mutex_lock_interruptible(&gspca_dev->queue_lock)) |
890 | mutex_lock_interruptible(&gspca_dev->queue_lock); | 892 | return -ERESTARTSYS; |
891 | gspca_stream_off(gspca_dev); | 893 | gspca_dev->users--; |
894 | if (gspca_dev->users > 0) { | ||
892 | mutex_unlock(&gspca_dev->queue_lock); | 895 | mutex_unlock(&gspca_dev->queue_lock); |
896 | return 0; | ||
893 | } | 897 | } |
894 | mutex_lock_interruptible(&gspca_dev->usb_lock); | 898 | |
899 | if (gspca_dev->streaming) | ||
900 | gspca_stream_off(gspca_dev); | ||
895 | gspca_dev->sd_desc->close(gspca_dev); | 901 | gspca_dev->sd_desc->close(gspca_dev); |
896 | mutex_unlock(&gspca_dev->usb_lock); | 902 | |
897 | atomic_inc(&gspca_dev->nevent); | ||
898 | wake_up_interruptible(&gspca_dev->wq); /* wake blocked processes */ | ||
899 | schedule(); | ||
900 | mutex_lock_interruptible(&gspca_dev->queue_lock); | ||
901 | frame_free(gspca_dev); | 903 | frame_free(gspca_dev); |
902 | file->private_data = NULL; | 904 | file->private_data = NULL; |
903 | gspca_dev->users--; | ||
904 | mutex_unlock(&gspca_dev->queue_lock); | 905 | mutex_unlock(&gspca_dev->queue_lock); |
905 | PDEBUG(D_STREAM, "closed"); | 906 | PDEBUG(D_STREAM, "closed"); |
906 | return 0; | 907 | return 0; |
@@ -964,7 +965,8 @@ static int vidioc_s_ctrl(struct file *file, void *priv, | |||
964 | && ctrl->value > ctrls->qctrl.maximum) | 965 | && ctrl->value > ctrls->qctrl.maximum) |
965 | return -ERANGE; | 966 | return -ERANGE; |
966 | PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); | 967 | PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); |
967 | mutex_lock_interruptible(&gspca_dev->usb_lock); | 968 | if (mutex_lock_interruptible(&gspca_dev->usb_lock)) |
969 | return -ERESTARTSYS; | ||
968 | ret = ctrls->set(gspca_dev, ctrl->value); | 970 | ret = ctrls->set(gspca_dev, ctrl->value); |
969 | mutex_unlock(&gspca_dev->usb_lock); | 971 | mutex_unlock(&gspca_dev->usb_lock); |
970 | return ret; | 972 | return ret; |
@@ -985,7 +987,8 @@ static int vidioc_g_ctrl(struct file *file, void *priv, | |||
985 | i++, ctrls++) { | 987 | i++, ctrls++) { |
986 | if (ctrl->id != ctrls->qctrl.id) | 988 | if (ctrl->id != ctrls->qctrl.id) |
987 | continue; | 989 | continue; |
988 | mutex_lock_interruptible(&gspca_dev->usb_lock); | 990 | if (mutex_lock_interruptible(&gspca_dev->usb_lock)) |
991 | return -ERESTARTSYS; | ||
989 | ret = ctrls->get(gspca_dev, &ctrl->value); | 992 | ret = ctrls->get(gspca_dev, &ctrl->value); |
990 | mutex_unlock(&gspca_dev->usb_lock); | 993 | mutex_unlock(&gspca_dev->usb_lock); |
991 | return ret; | 994 | return ret; |
@@ -1047,9 +1050,8 @@ static int vidioc_reqbufs(struct file *file, void *priv, | |||
1047 | frsz = gspca_get_buff_size(gspca_dev); | 1050 | frsz = gspca_get_buff_size(gspca_dev); |
1048 | if (frsz < 0) | 1051 | if (frsz < 0) |
1049 | return frsz; | 1052 | return frsz; |
1050 | ret = mutex_lock_interruptible(&gspca_dev->queue_lock); | 1053 | if (mutex_lock_interruptible(&gspca_dev->queue_lock)) |
1051 | if (ret < 0) | 1054 | return -ERESTARTSYS; |
1052 | return ret; | ||
1053 | ret = frame_alloc(gspca_dev, | 1055 | ret = frame_alloc(gspca_dev, |
1054 | rb->count, | 1056 | rb->count, |
1055 | (unsigned int) frsz, | 1057 | (unsigned int) frsz, |
@@ -1087,9 +1089,8 @@ static int vidioc_streamon(struct file *file, void *priv, | |||
1087 | PDEBUG(D_STREAM, "stream on"); | 1089 | PDEBUG(D_STREAM, "stream on"); |
1088 | if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | 1090 | if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
1089 | return -EINVAL; | 1091 | return -EINVAL; |
1090 | ret = mutex_lock_interruptible(&gspca_dev->queue_lock); | 1092 | if (mutex_lock_interruptible(&gspca_dev->queue_lock)) |
1091 | if (ret < 0) | 1093 | return -ERESTARTSYS; |
1092 | return ret; | ||
1093 | if (!gspca_dev->present) { | 1094 | if (!gspca_dev->present) { |
1094 | ret = -ENODEV; | 1095 | ret = -ENODEV; |
1095 | goto out; | 1096 | goto out; |
@@ -1111,6 +1112,7 @@ static int vidioc_streamon(struct file *file, void *priv, | |||
1111 | gspca_dev->height); | 1112 | gspca_dev->height); |
1112 | } | 1113 | } |
1113 | #endif | 1114 | #endif |
1115 | ret = 0; | ||
1114 | out: | 1116 | out: |
1115 | mutex_unlock(&gspca_dev->queue_lock); | 1117 | mutex_unlock(&gspca_dev->queue_lock); |
1116 | return ret; | 1118 | return ret; |
@@ -1125,8 +1127,14 @@ static int vidioc_streamoff(struct file *file, void *priv, | |||
1125 | if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) | 1127 | if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
1126 | return -EINVAL; | 1128 | return -EINVAL; |
1127 | if (gspca_dev->streaming) { | 1129 | if (gspca_dev->streaming) { |
1128 | mutex_lock_interruptible(&gspca_dev->queue_lock); | 1130 | if (mutex_lock_interruptible(&gspca_dev->queue_lock)) |
1131 | return -ERESTARTSYS; | ||
1132 | if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { | ||
1133 | mutex_unlock(&gspca_dev->queue_lock); | ||
1134 | return -ERESTARTSYS; | ||
1135 | } | ||
1129 | gspca_stream_off(gspca_dev); | 1136 | gspca_stream_off(gspca_dev); |
1137 | mutex_unlock(&gspca_dev->usb_lock); | ||
1130 | mutex_unlock(&gspca_dev->queue_lock); | 1138 | mutex_unlock(&gspca_dev->queue_lock); |
1131 | } | 1139 | } |
1132 | return 0; | 1140 | return 0; |
@@ -1140,7 +1148,8 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv, | |||
1140 | 1148 | ||
1141 | if (!gspca_dev->sd_desc->get_jcomp) | 1149 | if (!gspca_dev->sd_desc->get_jcomp) |
1142 | return -EINVAL; | 1150 | return -EINVAL; |
1143 | mutex_lock_interruptible(&gspca_dev->usb_lock); | 1151 | if (mutex_lock_interruptible(&gspca_dev->usb_lock)) |
1152 | return -ERESTARTSYS; | ||
1144 | ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); | 1153 | ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); |
1145 | mutex_unlock(&gspca_dev->usb_lock); | 1154 | mutex_unlock(&gspca_dev->usb_lock); |
1146 | return ret; | 1155 | return ret; |
@@ -1152,7 +1161,8 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, | |||
1152 | struct gspca_dev *gspca_dev = priv; | 1161 | struct gspca_dev *gspca_dev = priv; |
1153 | int ret; | 1162 | int ret; |
1154 | 1163 | ||
1155 | mutex_lock_interruptible(&gspca_dev->usb_lock); | 1164 | if (mutex_lock_interruptible(&gspca_dev->usb_lock)) |
1165 | return -ERESTARTSYS; | ||
1156 | if (!gspca_dev->sd_desc->set_jcomp) | 1166 | if (!gspca_dev->sd_desc->set_jcomp) |
1157 | return -EINVAL; | 1167 | return -EINVAL; |
1158 | ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); | 1168 | ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); |
@@ -1177,7 +1187,8 @@ static int vidioc_s_parm(struct file *filp, void *priv, | |||
1177 | struct gspca_dev *gspca_dev = priv; | 1187 | struct gspca_dev *gspca_dev = priv; |
1178 | int n; | 1188 | int n; |
1179 | 1189 | ||
1180 | mutex_lock_interruptible(&gspca_dev->usb_lock); | 1190 | if (mutex_lock_interruptible(&gspca_dev->usb_lock)) |
1191 | return -ERESTARTSYS; | ||
1181 | n = parm->parm.capture.readbuffers; | 1192 | n = parm->parm.capture.readbuffers; |
1182 | if (n == 0 || n > GSPCA_MAX_FRAMES) | 1193 | if (n == 0 || n > GSPCA_MAX_FRAMES) |
1183 | parm->parm.capture.readbuffers = gspca_dev->nbufread; | 1194 | parm->parm.capture.readbuffers = gspca_dev->nbufread; |
@@ -1230,10 +1241,8 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma) | |||
1230 | size = vma->vm_end - vma->vm_start; | 1241 | size = vma->vm_end - vma->vm_start; |
1231 | PDEBUG(D_STREAM, "mmap start:%08x size:%d", (int) start, (int) size); | 1242 | PDEBUG(D_STREAM, "mmap start:%08x size:%d", (int) start, (int) size); |
1232 | 1243 | ||
1233 | ret = mutex_lock_interruptible(&gspca_dev->queue_lock); | 1244 | if (mutex_lock_interruptible(&gspca_dev->queue_lock)) |
1234 | if (ret < 0) | 1245 | return -ERESTARTSYS; |
1235 | return ret; | ||
1236 | /* sanity check disconnect, in use, no memory available */ | ||
1237 | if (!gspca_dev->present) { | 1246 | if (!gspca_dev->present) { |
1238 | ret = -ENODEV; | 1247 | ret = -ENODEV; |
1239 | goto done; | 1248 | goto done; |
@@ -1294,6 +1303,7 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma) | |||
1294 | V4L2_BUF_FLAG_MAPPED; | 1303 | V4L2_BUF_FLAG_MAPPED; |
1295 | } | 1304 | } |
1296 | #endif | 1305 | #endif |
1306 | ret = 0; | ||
1297 | done: | 1307 | done: |
1298 | mutex_unlock(&gspca_dev->queue_lock); | 1308 | mutex_unlock(&gspca_dev->queue_lock); |
1299 | return ret; | 1309 | return ret; |
@@ -1350,6 +1360,8 @@ static int gspca_frame_wait(struct gspca_dev *gspca_dev, | |||
1350 | atomic_read(&gspca_dev->nevent) > 0); | 1360 | atomic_read(&gspca_dev->nevent) > 0); |
1351 | if (ret != 0) | 1361 | if (ret != 0) |
1352 | return ret; | 1362 | return ret; |
1363 | if (!gspca_dev->streaming || !gspca_dev->present) | ||
1364 | return -EIO; | ||
1353 | i = gspca_dev->fr_o; | 1365 | i = gspca_dev->fr_o; |
1354 | j = gspca_dev->fr_queue[i]; | 1366 | j = gspca_dev->fr_queue[i]; |
1355 | frame = &gspca_dev->frame[j]; | 1367 | frame = &gspca_dev->frame[j]; |
@@ -1364,6 +1376,10 @@ ok: | |||
1364 | gspca_dev->fr_q, | 1376 | gspca_dev->fr_q, |
1365 | gspca_dev->fr_i, | 1377 | gspca_dev->fr_i, |
1366 | gspca_dev->fr_o); | 1378 | gspca_dev->fr_o); |
1379 | |||
1380 | if (gspca_dev->sd_desc->dq_callback) | ||
1381 | gspca_dev->sd_desc->dq_callback(gspca_dev); | ||
1382 | |||
1367 | return j; | 1383 | return j; |
1368 | } | 1384 | } |
1369 | 1385 | ||
@@ -1435,9 +1451,9 @@ static int vidioc_qbuf(struct file *file, void *priv, | |||
1435 | return -EINVAL; | 1451 | return -EINVAL; |
1436 | } | 1452 | } |
1437 | 1453 | ||
1438 | ret = mutex_lock_interruptible(&gspca_dev->queue_lock); | 1454 | if (mutex_lock_interruptible(&gspca_dev->queue_lock)) |
1439 | if (ret < 0) | 1455 | return -ERESTARTSYS; |
1440 | return ret; | 1456 | |
1441 | if (frame->v4l2_buf.flags | 1457 | if (frame->v4l2_buf.flags |
1442 | & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)) { | 1458 | & (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)) { |
1443 | PDEBUG(D_STREAM, "qbuf bad state"); | 1459 | PDEBUG(D_STREAM, "qbuf bad state"); |
@@ -1708,11 +1724,12 @@ void gspca_disconnect(struct usb_interface *intf) | |||
1708 | if (!gspca_dev) | 1724 | if (!gspca_dev) |
1709 | return; | 1725 | return; |
1710 | gspca_dev->present = 0; | 1726 | gspca_dev->present = 0; |
1711 | mutex_lock_interruptible(&gspca_dev->queue_lock); | 1727 | mutex_lock(&gspca_dev->queue_lock); |
1712 | mutex_lock_interruptible(&gspca_dev->usb_lock); | 1728 | mutex_lock(&gspca_dev->usb_lock); |
1729 | gspca_dev->streaming = 0; | ||
1713 | gspca_kill_transfer(gspca_dev); | 1730 | gspca_kill_transfer(gspca_dev); |
1714 | mutex_unlock(&gspca_dev->queue_lock); | ||
1715 | mutex_unlock(&gspca_dev->usb_lock); | 1731 | mutex_unlock(&gspca_dev->usb_lock); |
1732 | mutex_unlock(&gspca_dev->queue_lock); | ||
1716 | while (gspca_dev->users != 0) { /* wait until fully closed */ | 1733 | while (gspca_dev->users != 0) { /* wait until fully closed */ |
1717 | atomic_inc(&gspca_dev->nevent); | 1734 | atomic_inc(&gspca_dev->nevent); |
1718 | wake_up_interruptible(&gspca_dev->wq); /* wake processes */ | 1735 | wake_up_interruptible(&gspca_dev->wq); /* wake processes */ |
diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 1394ab13f1f4..3bfb3641cf36 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h | |||
@@ -90,17 +90,18 @@ struct ctrl { | |||
90 | /* subdriver description */ | 90 | /* subdriver description */ |
91 | struct sd_desc { | 91 | struct sd_desc { |
92 | /* information */ | 92 | /* information */ |
93 | char *name; /* sub-driver name */ | 93 | char *name; /* sub-driver name */ |
94 | /* controls */ | 94 | /* controls */ |
95 | struct ctrl *ctrls; | 95 | struct ctrl *ctrls; |
96 | int nctrls; | 96 | int nctrls; |
97 | /* operations */ | 97 | /* operations */ |
98 | cam_cf_op config; /* called on probe */ | 98 | cam_cf_op config; /* called on probe */ |
99 | cam_op open; /* called on open */ | 99 | cam_op open; /* called on open */ |
100 | cam_v_op start; /* called on stream on */ | 100 | cam_v_op start; /* called on stream on */ |
101 | cam_v_op stopN; /* called on stream off - main alt */ | 101 | cam_v_op stopN; /* called on stream off - main alt */ |
102 | cam_v_op stop0; /* called on stream off - alt 0 */ | 102 | cam_v_op stop0; /* called on stream off - alt 0 */ |
103 | cam_v_op close; /* called on close */ | 103 | cam_v_op close; /* called on close */ |
104 | cam_v_op dq_callback; /* called when a frame has been dequeued */ | ||
104 | cam_pkt_op pkt_scan; | 105 | cam_pkt_op pkt_scan; |
105 | cam_jpg_op get_jcomp; | 106 | cam_jpg_op get_jcomp; |
106 | cam_jpg_op set_jcomp; | 107 | cam_jpg_op set_jcomp; |
@@ -167,8 +168,6 @@ int gspca_dev_probe(struct usb_interface *intf, | |||
167 | const struct usb_device_id *id, | 168 | const struct usb_device_id *id, |
168 | const struct sd_desc *sd_desc, | 169 | const struct sd_desc *sd_desc, |
169 | int dev_size); | 170 | int dev_size); |
170 | int gspca_dev_init(struct gspca_dev *gspca_dev, | ||
171 | struct usb_interface *intf); | ||
172 | void gspca_disconnect(struct usb_interface *intf); | 171 | void gspca_disconnect(struct usb_interface *intf); |
173 | struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, | 172 | struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, |
174 | int packet_type, | 173 | int packet_type, |
diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c new file mode 100644 index 000000000000..ac16c7352892 --- /dev/null +++ b/drivers/media/video/gspca/pac207.c | |||
@@ -0,0 +1,939 @@ | |||
1 | /* | ||
2 | * Pixart PAC207BCA library | ||
3 | * | ||
4 | * Copyright (C) 2008 Hans de Goede <j.w.r.degoede@hhs.nl> | ||
5 | * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li | ||
6 | * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr | ||
7 | * | ||
8 | * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | #define MODULE_NAME "pac207" | ||
27 | |||
28 | #include "gspca.h" | ||
29 | |||
30 | #define DRIVER_VERSION_NUMBER KERNEL_VERSION(0, 0, 30) | ||
31 | static const char version[] = "0.0.30"; | ||
32 | |||
33 | MODULE_AUTHOR("Hans de Goede <j.w.r.degoede@hhs.nl>"); | ||
34 | MODULE_DESCRIPTION("Pixart PAC207"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | |||
37 | #define PAC207_CTRL_TIMEOUT 100 /* ms */ | ||
38 | |||
39 | #define PAC207_BRIGHTNESS_MIN 0 | ||
40 | #define PAC207_BRIGHTNESS_MAX 255 | ||
41 | #define PAC207_BRIGHTNESS_DEFAULT 4 /* power on default: 4 */ | ||
42 | |||
43 | #define PAC207_EXPOSURE_MIN 4 | ||
44 | #define PAC207_EXPOSURE_MAX 26 | ||
45 | #define PAC207_EXPOSURE_DEFAULT 4 /* power on default: 3 ?? */ | ||
46 | #define PAC207_EXPOSURE_KNEE 11 /* 4 = 30 fps, 11 = 8, 15 = 6 */ | ||
47 | |||
48 | #define PAC207_GAIN_MIN 0 | ||
49 | #define PAC207_GAIN_MAX 31 | ||
50 | #define PAC207_GAIN_DEFAULT 9 /* power on default: 9 */ | ||
51 | #define PAC207_GAIN_KNEE 20 | ||
52 | |||
53 | #define PAC207_AUTOGAIN_DEADZONE 30 | ||
54 | /* We calculating the autogain at the end of the transfer of a frame, at this | ||
55 | moment a frame with the old settings is being transmitted, and a frame is | ||
56 | being captured with the old settings. So if we adjust the autogain we must | ||
57 | ignore atleast the 2 next frames for the new settings to come into effect | ||
58 | before doing any other adjustments */ | ||
59 | #define PAC207_AUTOGAIN_IGNORE_FRAMES 3 | ||
60 | |||
61 | enum pac207_line_state { | ||
62 | LINE_HEADER1, | ||
63 | LINE_HEADER2, | ||
64 | LINE_UNCOMPRESSED, | ||
65 | LINE_COMPRESSED, | ||
66 | }; | ||
67 | |||
68 | struct pac207_decoder_state { | ||
69 | /* generic state */ | ||
70 | u16 line_read; | ||
71 | u16 line_marker; | ||
72 | u8 line_state; | ||
73 | u8 header_read; | ||
74 | /* compression state */ | ||
75 | u16 processed_bytes; | ||
76 | u8 remaining_bits; | ||
77 | s8 no_remaining_bits; | ||
78 | u8 get_abs; | ||
79 | u8 discard_byte; | ||
80 | u8 line_decode_buf[352]; | ||
81 | }; | ||
82 | |||
83 | /* specific webcam descriptor */ | ||
84 | struct sd { | ||
85 | struct gspca_dev gspca_dev; /* !! must be the first item */ | ||
86 | |||
87 | struct pac207_decoder_state decoder_state; | ||
88 | |||
89 | u8 mode; | ||
90 | |||
91 | u8 brightness; | ||
92 | u8 exposure; | ||
93 | u8 autogain; | ||
94 | u8 gain; | ||
95 | |||
96 | u8 sof_read; | ||
97 | u8 autogain_ignore_frames; | ||
98 | |||
99 | atomic_t avg_lum; | ||
100 | }; | ||
101 | |||
102 | /* V4L2 controls supported by the driver */ | ||
103 | static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); | ||
104 | static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); | ||
105 | static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); | ||
106 | static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); | ||
107 | static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); | ||
108 | static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); | ||
109 | static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); | ||
110 | static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); | ||
111 | |||
112 | static struct ctrl sd_ctrls[] = { | ||
113 | #define SD_BRIGHTNESS 0 | ||
114 | { | ||
115 | { | ||
116 | .id = V4L2_CID_BRIGHTNESS, | ||
117 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
118 | .name = "Brightness", | ||
119 | .minimum = PAC207_BRIGHTNESS_MIN, | ||
120 | .maximum = PAC207_BRIGHTNESS_MAX, | ||
121 | .step = 1, | ||
122 | .default_value = PAC207_BRIGHTNESS_DEFAULT, | ||
123 | .flags = 0, | ||
124 | }, | ||
125 | .set = sd_setbrightness, | ||
126 | .get = sd_getbrightness, | ||
127 | }, | ||
128 | #define SD_EXPOSURE 1 | ||
129 | { | ||
130 | { | ||
131 | .id = V4L2_CID_EXPOSURE, | ||
132 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
133 | .name = "exposure", | ||
134 | .minimum = PAC207_EXPOSURE_MIN, | ||
135 | .maximum = PAC207_EXPOSURE_MAX, | ||
136 | .step = 1, | ||
137 | .default_value = PAC207_EXPOSURE_DEFAULT, | ||
138 | .flags = 0, | ||
139 | }, | ||
140 | .set = sd_setexposure, | ||
141 | .get = sd_getexposure, | ||
142 | }, | ||
143 | #define SD_AUTOGAIN 2 | ||
144 | { | ||
145 | { | ||
146 | .id = V4L2_CID_AUTOGAIN, | ||
147 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
148 | .name = "Auto Gain", | ||
149 | .minimum = 0, | ||
150 | .maximum = 1, | ||
151 | .step = 1, | ||
152 | .default_value = 1, | ||
153 | .flags = 0, | ||
154 | }, | ||
155 | .set = sd_setautogain, | ||
156 | .get = sd_getautogain, | ||
157 | }, | ||
158 | #define SD_GAIN 3 | ||
159 | { | ||
160 | { | ||
161 | .id = V4L2_CID_GAIN, | ||
162 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
163 | .name = "gain", | ||
164 | .minimum = PAC207_GAIN_MIN, | ||
165 | .maximum = PAC207_GAIN_MAX, | ||
166 | .step = 1, | ||
167 | .default_value = PAC207_GAIN_DEFAULT, | ||
168 | .flags = 0, | ||
169 | }, | ||
170 | .set = sd_setgain, | ||
171 | .get = sd_getgain, | ||
172 | }, | ||
173 | }; | ||
174 | |||
175 | static struct cam_mode sif_mode[] = { | ||
176 | {V4L2_PIX_FMT_SBGGR8, 176, 144, 1}, | ||
177 | {V4L2_PIX_FMT_SBGGR8, 352, 288, 0}, | ||
178 | }; | ||
179 | |||
180 | static const __u8 pac207_sensor_init[][8] = { | ||
181 | {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0}, | ||
182 | {0x00, 0x64, 0x64, 0x64, 0x04, 0x10, 0xf0, 0x30}, | ||
183 | {0x00, 0x00, 0x00, 0x70, 0xa0, 0xf8, 0x00, 0x00}, | ||
184 | {0x00, 0x00, 0x32, 0x00, 0x96, 0x00, 0xa2, 0x02}, | ||
185 | {0x32, 0x00, 0x96, 0x00, 0xA2, 0x02, 0xaf, 0x00}, | ||
186 | }; | ||
187 | |||
188 | /* 48 reg_72 Rate Control end BalSize_4a =0x36 */ | ||
189 | static const __u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 }; | ||
190 | |||
191 | static const char pac207_sof_marker[5] = { 0xff, 0xff, 0x00, 0xff, 0x96 }; | ||
192 | |||
193 | int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, | ||
194 | const u8 *buffer, u16 length) | ||
195 | { | ||
196 | struct usb_device *udev = gspca_dev->dev; | ||
197 | int err; | ||
198 | u8 kbuf[8]; | ||
199 | |||
200 | memcpy(kbuf, buffer, length); | ||
201 | |||
202 | err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01, | ||
203 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, | ||
204 | 0x00, index, kbuf, length, PAC207_CTRL_TIMEOUT); | ||
205 | if (err < 0) | ||
206 | PDEBUG(D_ERR, | ||
207 | "Failed to write registers to index 0x%04X, error %d)", | ||
208 | index, err); | ||
209 | |||
210 | return err; | ||
211 | } | ||
212 | |||
213 | |||
214 | int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) | ||
215 | { | ||
216 | struct usb_device *udev = gspca_dev->dev; | ||
217 | int err; | ||
218 | |||
219 | err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, | ||
220 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, | ||
221 | value, index, NULL, 0, PAC207_CTRL_TIMEOUT); | ||
222 | if (err) | ||
223 | PDEBUG(D_ERR, "Failed to write a register (index 0x%04X," | ||
224 | " value 0x%02X, error %d)", index, value, err); | ||
225 | |||
226 | return err; | ||
227 | } | ||
228 | |||
229 | |||
230 | int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) | ||
231 | { | ||
232 | struct usb_device *udev = gspca_dev->dev; | ||
233 | u8 buff; | ||
234 | int res; | ||
235 | |||
236 | res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, | ||
237 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, | ||
238 | 0x00, index, &buff, 1, PAC207_CTRL_TIMEOUT); | ||
239 | if (res < 0) { | ||
240 | PDEBUG(D_ERR, | ||
241 | "Failed to read a register (index 0x%04X, error %d)", | ||
242 | index, res); | ||
243 | return res; | ||
244 | } | ||
245 | |||
246 | return buff; | ||
247 | } | ||
248 | |||
249 | |||
250 | /* this function is called at probe time */ | ||
251 | static int sd_config(struct gspca_dev *gspca_dev, | ||
252 | const struct usb_device_id *id) | ||
253 | { | ||
254 | struct cam *cam; | ||
255 | u8 idreg[2]; | ||
256 | |||
257 | idreg[0] = pac207_read_reg(gspca_dev, 0x0000); | ||
258 | idreg[1] = pac207_read_reg(gspca_dev, 0x0001); | ||
259 | idreg[0] = ((idreg[0] & 0x0F) << 4) | ((idreg[1] & 0xf0) >> 4); | ||
260 | idreg[1] = idreg[1] & 0x0f; | ||
261 | PDEBUG(D_PROBE, "Pixart Sensor ID 0x%02X Chips ID 0x%02X", | ||
262 | idreg[0], idreg[1]); | ||
263 | |||
264 | if (idreg[0] != 0x27) { | ||
265 | PDEBUG(D_PROBE, "Error invalid sensor ID!"); | ||
266 | return -ENODEV; | ||
267 | } | ||
268 | |||
269 | pac207_write_reg(gspca_dev, 0x41, 0x00); | ||
270 | /* Bit_0=Image Format, | ||
271 | * Bit_1=LED, | ||
272 | * Bit_2=Compression test mode enable */ | ||
273 | pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ | ||
274 | pac207_write_reg(gspca_dev, 0x11, 0x30); /* Analog Bias */ | ||
275 | |||
276 | PDEBUG(D_PROBE, | ||
277 | "Pixart PAC207BCA Image Processor and Control Chip detected" | ||
278 | " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); | ||
279 | |||
280 | cam = &gspca_dev->cam; | ||
281 | cam->dev_name = (char *) id->driver_info; | ||
282 | cam->epaddr = 0x05; | ||
283 | cam->cam_mode = sif_mode; | ||
284 | cam->nmodes = ARRAY_SIZE(sif_mode); | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | /* this function is called at open time */ | ||
290 | static int sd_open(struct gspca_dev *gspca_dev) | ||
291 | { | ||
292 | struct sd *sd = (struct sd *) gspca_dev; | ||
293 | |||
294 | sd->brightness = PAC207_BRIGHTNESS_DEFAULT; | ||
295 | sd->exposure = PAC207_EXPOSURE_DEFAULT; | ||
296 | sd->gain = PAC207_GAIN_DEFAULT; | ||
297 | sd->autogain = 1; | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | /* -- start the camera -- */ | ||
303 | static void sd_start(struct gspca_dev *gspca_dev) | ||
304 | { | ||
305 | struct sd *sd = (struct sd *) gspca_dev; | ||
306 | __u8 mode; | ||
307 | |||
308 | pac207_write_reg(gspca_dev, 0x0f, 0x10); /* Power control (Bit 6-0) */ | ||
309 | pac207_write_regs(gspca_dev, 0x0002, pac207_sensor_init[0], 8); | ||
310 | pac207_write_regs(gspca_dev, 0x000a, pac207_sensor_init[1], 8); | ||
311 | pac207_write_regs(gspca_dev, 0x0012, pac207_sensor_init[2], 8); | ||
312 | pac207_write_regs(gspca_dev, 0x0040, pac207_sensor_init[3], 8); | ||
313 | pac207_write_regs(gspca_dev, 0x0042, pac207_sensor_init[4], 8); | ||
314 | pac207_write_regs(gspca_dev, 0x0048, PacReg72, 4); | ||
315 | |||
316 | /* Compression Balance */ | ||
317 | if (gspca_dev->width == 176) | ||
318 | pac207_write_reg(gspca_dev, 0x4a, 0xff); | ||
319 | else | ||
320 | pac207_write_reg(gspca_dev, 0x4a, 0x88); | ||
321 | pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */ | ||
322 | pac207_write_reg(gspca_dev, 0x08, sd->brightness); | ||
323 | |||
324 | /* PGA global gain (Bit 4-0) */ | ||
325 | pac207_write_reg(gspca_dev, 0x0e, sd->gain); | ||
326 | pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */ | ||
327 | |||
328 | mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */ | ||
329 | if (gspca_dev->width == 176) { /* 176x144 */ | ||
330 | mode |= 0x01; | ||
331 | PDEBUG(D_STREAM, "pac207_start mode 176x144"); | ||
332 | } else/* 352x288 */ | ||
333 | PDEBUG(D_STREAM, "pac207_start mode 352x288"); | ||
334 | pac207_write_reg(gspca_dev, 0x41, mode); | ||
335 | |||
336 | pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ | ||
337 | pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ | ||
338 | udelay(1000); /* taken from gspca */ | ||
339 | pac207_write_reg(gspca_dev, 0x40, 0x01); /* Start ISO pipe */ | ||
340 | |||
341 | sd->sof_read = 0; | ||
342 | sd->autogain_ignore_frames = 0; | ||
343 | atomic_set(&sd->avg_lum, -1); | ||
344 | } | ||
345 | |||
346 | static void sd_stopN(struct gspca_dev *gspca_dev) | ||
347 | { | ||
348 | pac207_write_reg(gspca_dev, 0x40, 0x00); /* Stop ISO pipe */ | ||
349 | pac207_write_reg(gspca_dev, 0x41, 0x00); /* Turn of LED */ | ||
350 | pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ | ||
351 | } | ||
352 | |||
353 | static void sd_stop0(struct gspca_dev *gspca_dev) | ||
354 | { | ||
355 | } | ||
356 | |||
357 | /* this function is called at close time */ | ||
358 | static void sd_close(struct gspca_dev *gspca_dev) | ||
359 | { | ||
360 | } | ||
361 | |||
362 | /* -- convert pixart frames to Bayer -- */ | ||
363 | /* Sonix decompressor struct B.S.(2004) */ | ||
364 | static struct { | ||
365 | u8 is_abs; | ||
366 | u8 len; | ||
367 | s8 val; | ||
368 | } table[256]; | ||
369 | |||
370 | void init_pixart_decoder(void) | ||
371 | { | ||
372 | int i, is_abs, val, len; | ||
373 | |||
374 | for (i = 0; i < 256; i++) { | ||
375 | is_abs = 0; | ||
376 | val = 0; | ||
377 | len = 0; | ||
378 | if ((i & 0xC0) == 0) { | ||
379 | /* code 00 */ | ||
380 | val = 0; | ||
381 | len = 2; | ||
382 | } else if ((i & 0xC0) == 0x40) { | ||
383 | /* code 01 */ | ||
384 | val = -5; | ||
385 | len = 2; | ||
386 | } else if ((i & 0xC0) == 0x80) { | ||
387 | /* code 10 */ | ||
388 | val = 5; | ||
389 | len = 2; | ||
390 | } else if ((i & 0xF0) == 0xC0) { | ||
391 | /* code 1100 */ | ||
392 | val = -10; | ||
393 | len = 4; | ||
394 | } else if ((i & 0xF0) == 0xD0) { | ||
395 | /* code 1101 */ | ||
396 | val = 10; | ||
397 | len = 4; | ||
398 | } else if ((i & 0xF8) == 0xE0) { | ||
399 | /* code 11100 */ | ||
400 | val = -15; | ||
401 | len = 5; | ||
402 | } else if ((i & 0xF8) == 0xE8) { | ||
403 | /* code 11101 */ | ||
404 | val = 15; | ||
405 | len = 5; | ||
406 | } else if ((i & 0xFC) == 0xF0) { | ||
407 | /* code 111100 */ | ||
408 | val = -20; | ||
409 | len = 6; | ||
410 | } else if ((i & 0xFC) == 0xF4) { | ||
411 | /* code 111101 */ | ||
412 | val = 20; | ||
413 | len = 6; | ||
414 | } else if ((i & 0xF8) == 0xF8) { | ||
415 | /* code 11111xxxxxx */ | ||
416 | is_abs = 1; | ||
417 | val = 0; | ||
418 | len = 5; | ||
419 | } | ||
420 | table[i].is_abs = is_abs; | ||
421 | table[i].val = val; | ||
422 | table[i].len = len; | ||
423 | } | ||
424 | } | ||
425 | |||
426 | /* auto gain and exposure algorithm based on the knee algorithm described here: | ||
427 | http://ytse.tricolour.net/docs/LowLightOptimization.html */ | ||
428 | static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) | ||
429 | { | ||
430 | struct sd *sd = (struct sd *) gspca_dev; | ||
431 | int i, steps, desired_avg_lum; | ||
432 | int orig_gain = sd->gain; | ||
433 | int orig_exposure = sd->exposure; | ||
434 | int avg_lum = atomic_read(&sd->avg_lum); | ||
435 | |||
436 | if (!sd->autogain || avg_lum == -1) | ||
437 | return; | ||
438 | |||
439 | if (sd->autogain_ignore_frames > 0) { | ||
440 | sd->autogain_ignore_frames--; | ||
441 | return; | ||
442 | } | ||
443 | |||
444 | /* correct desired lumination for the configured brightness */ | ||
445 | desired_avg_lum = 100 + sd->brightness / 2; | ||
446 | |||
447 | /* If we are of a multiple of deadzone, do multiple step to reach the | ||
448 | desired lumination fast (with the risc of a slight overshoot) */ | ||
449 | steps = abs(desired_avg_lum - avg_lum) / PAC207_AUTOGAIN_DEADZONE; | ||
450 | |||
451 | for (i = 0; i < steps; i++) { | ||
452 | if (avg_lum > desired_avg_lum) { | ||
453 | if (sd->gain > PAC207_GAIN_KNEE) { | ||
454 | sd->gain--; | ||
455 | } else if (sd->exposure > PAC207_EXPOSURE_KNEE) { | ||
456 | sd->exposure--; | ||
457 | } else if (sd->gain > PAC207_GAIN_DEFAULT) { | ||
458 | sd->gain--; | ||
459 | } else if (sd->exposure > PAC207_EXPOSURE_MIN) { | ||
460 | sd->exposure--; | ||
461 | } else if (sd->gain > PAC207_GAIN_MIN) { | ||
462 | sd->gain--; | ||
463 | } else | ||
464 | break; | ||
465 | } else { | ||
466 | if (sd->gain < PAC207_GAIN_DEFAULT) { | ||
467 | sd->gain++; | ||
468 | } else if (sd->exposure < PAC207_EXPOSURE_KNEE) { | ||
469 | sd->exposure++; | ||
470 | } else if (sd->gain < PAC207_GAIN_KNEE) { | ||
471 | sd->gain++; | ||
472 | } else if (sd->exposure < PAC207_EXPOSURE_MAX) { | ||
473 | sd->exposure++; | ||
474 | } else if (sd->gain < PAC207_GAIN_MAX) { | ||
475 | sd->gain++; | ||
476 | } else | ||
477 | break; | ||
478 | } | ||
479 | } | ||
480 | |||
481 | if (sd->exposure != orig_exposure || sd->gain != orig_gain) { | ||
482 | if (sd->exposure != orig_exposure) | ||
483 | pac207_write_reg(gspca_dev, 0x0002, sd->exposure); | ||
484 | if (sd->gain != orig_gain) | ||
485 | pac207_write_reg(gspca_dev, 0x000e, sd->gain); | ||
486 | pac207_write_reg(gspca_dev, 0x13, 0x01); /* load reg to sen */ | ||
487 | pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ | ||
488 | sd->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES; | ||
489 | } | ||
490 | } | ||
491 | |||
492 | static unsigned char *pac207_find_sof(struct gspca_dev *gspca_dev, | ||
493 | unsigned char *m, int len) | ||
494 | { | ||
495 | struct sd *sd = (struct sd *) gspca_dev; | ||
496 | int i; | ||
497 | |||
498 | /* Search for the SOF marker (fixed part) in the header */ | ||
499 | for (i = 0; i < len; i++) { | ||
500 | if (m[i] == pac207_sof_marker[sd->sof_read]) { | ||
501 | sd->sof_read++; | ||
502 | if (sd->sof_read == sizeof(pac207_sof_marker)) { | ||
503 | PDEBUG(D_STREAM, | ||
504 | "SOF found, bytes to analyze: %u." | ||
505 | " Frame starts at byte #%u", | ||
506 | len, i + 1); | ||
507 | sd->sof_read = 0; | ||
508 | return m + i + 1; | ||
509 | } | ||
510 | } else | ||
511 | sd->sof_read = 0; | ||
512 | } | ||
513 | |||
514 | return NULL; | ||
515 | } | ||
516 | |||
517 | static int pac207_decompress_row(struct gspca_dev *gspca_dev, | ||
518 | struct gspca_frame *f, unsigned char *cdata, int len) | ||
519 | { | ||
520 | struct sd *sd = (struct sd *) gspca_dev; | ||
521 | struct pac207_decoder_state *decoder_state = &sd->decoder_state; | ||
522 | unsigned char *outp = decoder_state->line_decode_buf + | ||
523 | decoder_state->line_read; | ||
524 | int val, bitlen, bitpos = -decoder_state->no_remaining_bits; | ||
525 | u8 code; | ||
526 | |||
527 | /* first two pixels are stored as raw 8-bit */ | ||
528 | while (decoder_state->line_read < 2) { | ||
529 | *outp++ = *cdata++; | ||
530 | decoder_state->line_read++; | ||
531 | len--; | ||
532 | if (len == 0) | ||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | while (decoder_state->line_read < gspca_dev->width) { | ||
537 | if (bitpos < 0) { | ||
538 | code = decoder_state->remaining_bits << (8 + bitpos) | | ||
539 | cdata[0] >> -bitpos; | ||
540 | } else { | ||
541 | u8 *addr = cdata + bitpos / 8; | ||
542 | code = addr[0] << (bitpos & 7) | | ||
543 | addr[1] >> (8 - (bitpos & 7)); | ||
544 | } | ||
545 | |||
546 | bitlen = decoder_state->get_abs ? | ||
547 | 6 : table[code].len; | ||
548 | |||
549 | /* Stop decompressing if we're out of input data */ | ||
550 | if ((bitpos + bitlen) > (len * 8)) | ||
551 | break; | ||
552 | |||
553 | if (decoder_state->get_abs) { | ||
554 | *outp++ = code & 0xFC; | ||
555 | decoder_state->line_read++; | ||
556 | decoder_state->get_abs = 0; | ||
557 | } else { | ||
558 | if (table[code].is_abs) | ||
559 | decoder_state->get_abs = 1; | ||
560 | else { | ||
561 | /* relative to left pixel */ | ||
562 | val = outp[-2] + | ||
563 | table[code].val; | ||
564 | if (val > 0xff) | ||
565 | val = 0xff; | ||
566 | else if (val < 0) | ||
567 | val = 0; | ||
568 | *outp++ = val; | ||
569 | decoder_state->line_read++; | ||
570 | } | ||
571 | } | ||
572 | bitpos += bitlen; | ||
573 | } | ||
574 | |||
575 | if (decoder_state->line_read == gspca_dev->width) { | ||
576 | int compressed_line_len; | ||
577 | |||
578 | gspca_frame_add(gspca_dev, INTER_PACKET, f, | ||
579 | decoder_state->line_decode_buf, | ||
580 | gspca_dev->width); | ||
581 | |||
582 | /* completely decompressed line, round pos to nearest word */ | ||
583 | compressed_line_len = ((decoder_state->processed_bytes * 8 + | ||
584 | bitpos + 15) / 16) * 2; | ||
585 | |||
586 | len -= compressed_line_len - decoder_state->processed_bytes; | ||
587 | if (len < 0) { | ||
588 | decoder_state->discard_byte = 1; | ||
589 | len = 0; | ||
590 | } | ||
591 | } else { | ||
592 | decoder_state->processed_bytes += len; | ||
593 | decoder_state->remaining_bits = cdata[bitpos/8]; | ||
594 | decoder_state->no_remaining_bits = (8 - bitpos) & 7; | ||
595 | len = 0; | ||
596 | } | ||
597 | |||
598 | return len; | ||
599 | } | ||
600 | |||
601 | static void pac207_decode_line_init(struct gspca_dev *gspca_dev) | ||
602 | { | ||
603 | struct sd *sd = (struct sd *) gspca_dev; | ||
604 | struct pac207_decoder_state *decoder_state = &sd->decoder_state; | ||
605 | |||
606 | decoder_state->line_read = 0; | ||
607 | decoder_state->line_state = LINE_HEADER1; | ||
608 | decoder_state->processed_bytes = 0; | ||
609 | decoder_state->no_remaining_bits = 0; | ||
610 | decoder_state->get_abs = 0; | ||
611 | } | ||
612 | |||
613 | static void pac207_decode_frame_init(struct gspca_dev *gspca_dev) | ||
614 | { | ||
615 | struct sd *sd = (struct sd *) gspca_dev; | ||
616 | struct pac207_decoder_state *decoder_state = &sd->decoder_state; | ||
617 | |||
618 | decoder_state->header_read = 0; | ||
619 | decoder_state->discard_byte = 0; | ||
620 | |||
621 | pac207_decode_line_init(gspca_dev); | ||
622 | } | ||
623 | |||
624 | static int pac207_decode_frame_data(struct gspca_dev *gspca_dev, | ||
625 | struct gspca_frame *f, unsigned char *data, int len) | ||
626 | { | ||
627 | struct sd *sd = (struct sd *) gspca_dev; | ||
628 | struct pac207_decoder_state *decoder_state = &sd->decoder_state; | ||
629 | int needed = 0; | ||
630 | |||
631 | /* first 11 bytes after sof marker: frame header */ | ||
632 | if (decoder_state->header_read < 11) { | ||
633 | /* get average lumination from frame header (byte 5) */ | ||
634 | if (decoder_state->header_read < 5) { | ||
635 | needed = 5 - decoder_state->header_read; | ||
636 | if (len >= needed) | ||
637 | atomic_set(&sd->avg_lum, data[needed-1]); | ||
638 | } | ||
639 | /* skip the rest of the header */ | ||
640 | needed = 11 - decoder_state->header_read; | ||
641 | if (len <= needed) { | ||
642 | decoder_state->header_read += len; | ||
643 | return 0; | ||
644 | } | ||
645 | data += needed; | ||
646 | len -= needed; | ||
647 | decoder_state->header_read = 11; | ||
648 | } | ||
649 | |||
650 | while (len) { | ||
651 | if (decoder_state->discard_byte) { | ||
652 | data++; | ||
653 | len--; | ||
654 | decoder_state->discard_byte = 0; | ||
655 | continue; | ||
656 | } | ||
657 | |||
658 | switch (decoder_state->line_state) { | ||
659 | case LINE_HEADER1: | ||
660 | decoder_state->line_marker = data[0] << 8; | ||
661 | decoder_state->line_state = LINE_HEADER2; | ||
662 | needed = 1; | ||
663 | break; | ||
664 | case LINE_HEADER2: | ||
665 | decoder_state->line_marker |= data[0]; | ||
666 | switch (decoder_state->line_marker) { | ||
667 | case 0x0FF0: | ||
668 | decoder_state->line_state = LINE_UNCOMPRESSED; | ||
669 | break; | ||
670 | case 0x1EE1: | ||
671 | decoder_state->line_state = LINE_COMPRESSED; | ||
672 | break; | ||
673 | default: | ||
674 | PDEBUG(D_STREAM, | ||
675 | "Error unknown line-header %04X", | ||
676 | (int) decoder_state->line_marker); | ||
677 | gspca_dev->last_packet_type = DISCARD_PACKET; | ||
678 | return 0; | ||
679 | } | ||
680 | needed = 1; | ||
681 | break; | ||
682 | case LINE_UNCOMPRESSED: | ||
683 | needed = gspca_dev->width - decoder_state->line_read; | ||
684 | if (needed > len) | ||
685 | needed = len; | ||
686 | gspca_frame_add(gspca_dev, INTER_PACKET, f, data, | ||
687 | needed); | ||
688 | decoder_state->line_read += needed; | ||
689 | break; | ||
690 | case LINE_COMPRESSED: | ||
691 | needed = len - | ||
692 | pac207_decompress_row(gspca_dev, f, data, len); | ||
693 | break; | ||
694 | } | ||
695 | |||
696 | data += needed; | ||
697 | len -= needed; | ||
698 | |||
699 | if (decoder_state->line_read == gspca_dev->width) { | ||
700 | if ((f->data_end - f->data) == | ||
701 | (gspca_dev->width * gspca_dev->height)) { | ||
702 | /* eureka we've got a frame */ | ||
703 | return 1; | ||
704 | } | ||
705 | pac207_decode_line_init(gspca_dev); | ||
706 | } | ||
707 | } | ||
708 | |||
709 | return 0; | ||
710 | } | ||
711 | |||
712 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, | ||
713 | struct gspca_frame *frame, | ||
714 | unsigned char *data, | ||
715 | int len) | ||
716 | { | ||
717 | unsigned char *sof; | ||
718 | int n; | ||
719 | |||
720 | sof = pac207_find_sof(gspca_dev, data, len); | ||
721 | |||
722 | if (sof) { | ||
723 | /* finish decoding current frame */ | ||
724 | if (gspca_dev->last_packet_type == INTER_PACKET) { | ||
725 | n = sof - data; | ||
726 | if (n > sizeof(pac207_sof_marker)) | ||
727 | n -= sizeof(pac207_sof_marker); | ||
728 | else | ||
729 | n = 0; | ||
730 | n = pac207_decode_frame_data(gspca_dev, frame, | ||
731 | data, n); | ||
732 | if (n) | ||
733 | frame = gspca_frame_add(gspca_dev, | ||
734 | LAST_PACKET, | ||
735 | frame, | ||
736 | NULL, | ||
737 | 0); | ||
738 | else | ||
739 | PDEBUG(D_STREAM, "Incomplete frame"); | ||
740 | } | ||
741 | pac207_decode_frame_init(gspca_dev); | ||
742 | gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, | ||
743 | 0); | ||
744 | len -= sof - data; | ||
745 | data = sof; | ||
746 | } | ||
747 | |||
748 | if (gspca_dev->last_packet_type == DISCARD_PACKET) | ||
749 | return; | ||
750 | |||
751 | n = pac207_decode_frame_data(gspca_dev, frame, data, len); | ||
752 | if (n) | ||
753 | frame = gspca_frame_add(gspca_dev, LAST_PACKET, | ||
754 | frame, NULL, 0); | ||
755 | } | ||
756 | |||
757 | static void setbrightness(struct gspca_dev *gspca_dev) | ||
758 | { | ||
759 | struct sd *sd = (struct sd *) gspca_dev; | ||
760 | |||
761 | pac207_write_reg(gspca_dev, 0x08, sd->brightness); | ||
762 | pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ | ||
763 | pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ | ||
764 | } | ||
765 | |||
766 | static void setexposure(struct gspca_dev *gspca_dev) | ||
767 | { | ||
768 | struct sd *sd = (struct sd *) gspca_dev; | ||
769 | |||
770 | pac207_write_reg(gspca_dev, 0x02, sd->exposure); | ||
771 | pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ | ||
772 | pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ | ||
773 | } | ||
774 | |||
775 | static void setgain(struct gspca_dev *gspca_dev) | ||
776 | { | ||
777 | struct sd *sd = (struct sd *) gspca_dev; | ||
778 | |||
779 | pac207_write_reg(gspca_dev, 0x0e, sd->gain); | ||
780 | pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ | ||
781 | pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ | ||
782 | } | ||
783 | |||
784 | static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) | ||
785 | { | ||
786 | struct sd *sd = (struct sd *) gspca_dev; | ||
787 | |||
788 | sd->brightness = val; | ||
789 | if (gspca_dev->streaming) | ||
790 | setbrightness(gspca_dev); | ||
791 | return 0; | ||
792 | } | ||
793 | |||
794 | static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) | ||
795 | { | ||
796 | struct sd *sd = (struct sd *) gspca_dev; | ||
797 | |||
798 | *val = sd->brightness; | ||
799 | return 0; | ||
800 | } | ||
801 | |||
802 | static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) | ||
803 | { | ||
804 | struct sd *sd = (struct sd *) gspca_dev; | ||
805 | |||
806 | /* don't allow mucking with exposure when using autogain */ | ||
807 | if (sd->autogain) | ||
808 | return -EINVAL; | ||
809 | |||
810 | sd->exposure = val; | ||
811 | if (gspca_dev->streaming) | ||
812 | setexposure(gspca_dev); | ||
813 | return 0; | ||
814 | } | ||
815 | |||
816 | static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) | ||
817 | { | ||
818 | struct sd *sd = (struct sd *) gspca_dev; | ||
819 | |||
820 | *val = sd->exposure; | ||
821 | return 0; | ||
822 | } | ||
823 | |||
824 | static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) | ||
825 | { | ||
826 | struct sd *sd = (struct sd *) gspca_dev; | ||
827 | |||
828 | /* don't allow mucking with gain when using autogain */ | ||
829 | if (sd->autogain) | ||
830 | return -EINVAL; | ||
831 | |||
832 | sd->gain = val; | ||
833 | if (gspca_dev->streaming) | ||
834 | setgain(gspca_dev); | ||
835 | return 0; | ||
836 | } | ||
837 | |||
838 | static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) | ||
839 | { | ||
840 | struct sd *sd = (struct sd *) gspca_dev; | ||
841 | |||
842 | *val = sd->gain; | ||
843 | return 0; | ||
844 | } | ||
845 | |||
846 | static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) | ||
847 | { | ||
848 | struct sd *sd = (struct sd *) gspca_dev; | ||
849 | |||
850 | sd->autogain = val; | ||
851 | /* when switching to autogain set defaults to make sure | ||
852 | we are on a valid point of the autogain gain / | ||
853 | exposure knee graph, and give this change time to | ||
854 | take effect before doing autogain. */ | ||
855 | if (sd->autogain) { | ||
856 | sd->exposure = PAC207_EXPOSURE_DEFAULT; | ||
857 | sd->gain = PAC207_GAIN_DEFAULT; | ||
858 | if (gspca_dev->streaming) { | ||
859 | sd->autogain_ignore_frames = | ||
860 | PAC207_AUTOGAIN_IGNORE_FRAMES; | ||
861 | setexposure(gspca_dev); | ||
862 | setgain(gspca_dev); | ||
863 | } | ||
864 | } | ||
865 | |||
866 | return 0; | ||
867 | } | ||
868 | |||
869 | static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) | ||
870 | { | ||
871 | struct sd *sd = (struct sd *) gspca_dev; | ||
872 | |||
873 | *val = sd->autogain; | ||
874 | return 0; | ||
875 | } | ||
876 | |||
877 | /* sub-driver description */ | ||
878 | static struct sd_desc sd_desc = { | ||
879 | .name = MODULE_NAME, | ||
880 | .ctrls = sd_ctrls, | ||
881 | .nctrls = ARRAY_SIZE(sd_ctrls), | ||
882 | .config = sd_config, | ||
883 | .open = sd_open, | ||
884 | .start = sd_start, | ||
885 | .stopN = sd_stopN, | ||
886 | .stop0 = sd_stop0, | ||
887 | .close = sd_close, | ||
888 | .dq_callback = pac207_do_auto_gain, | ||
889 | .pkt_scan = sd_pkt_scan, | ||
890 | }; | ||
891 | |||
892 | /* -- module initialisation -- */ | ||
893 | #define DVNM(name) .driver_info = (kernel_ulong_t) name | ||
894 | static __devinitdata struct usb_device_id device_table[] = { | ||
895 | {USB_DEVICE(0x041e, 0x4028), DVNM("Creative Webcam Vista Plus")}, | ||
896 | {USB_DEVICE(0x093a, 0x2460), DVNM("PAC207 Qtec Webcam 100")}, | ||
897 | {USB_DEVICE(0x093a, 0x2463), DVNM("Philips spc200nc pac207")}, | ||
898 | {USB_DEVICE(0x093a, 0x2464), DVNM("Labtec Webcam 1200")}, | ||
899 | {USB_DEVICE(0x093a, 0x2468), DVNM("PAC207")}, | ||
900 | {USB_DEVICE(0x093a, 0x2470), DVNM("Genius GF112")}, | ||
901 | {USB_DEVICE(0x093a, 0x2471), DVNM("PAC207 Genius VideoCam ge111")}, | ||
902 | {USB_DEVICE(0x093a, 0x2472), DVNM("PAC207 Genius VideoCam ge110")}, | ||
903 | {USB_DEVICE(0x2001, 0xf115), DVNM("D-Link DSB-C120")}, | ||
904 | {} | ||
905 | }; | ||
906 | MODULE_DEVICE_TABLE(usb, device_table); | ||
907 | |||
908 | /* -- device connect -- */ | ||
909 | static int sd_probe(struct usb_interface *intf, | ||
910 | const struct usb_device_id *id) | ||
911 | { | ||
912 | PDEBUG(D_PROBE, "camera probe"); | ||
913 | return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd)); | ||
914 | } | ||
915 | |||
916 | static struct usb_driver sd_driver = { | ||
917 | .name = MODULE_NAME, | ||
918 | .id_table = device_table, | ||
919 | .probe = sd_probe, | ||
920 | .disconnect = gspca_disconnect, | ||
921 | }; | ||
922 | |||
923 | /* -- module insert / remove -- */ | ||
924 | static int __init sd_mod_init(void) | ||
925 | { | ||
926 | init_pixart_decoder(); | ||
927 | if (usb_register(&sd_driver) < 0) | ||
928 | return -1; | ||
929 | PDEBUG(D_PROBE, "v%s registered", version); | ||
930 | return 0; | ||
931 | } | ||
932 | static void __exit sd_mod_exit(void) | ||
933 | { | ||
934 | usb_deregister(&sd_driver); | ||
935 | PDEBUG(D_PROBE, "deregistered"); | ||
936 | } | ||
937 | |||
938 | module_init(sd_mod_init); | ||
939 | module_exit(sd_mod_exit); | ||