1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * spca1528 subdriver |
4 | * |
5 | * Copyright (C) 2010-2011 Jean-Francois Moine (http://moinejf.free.fr) |
6 | */ |
7 | |
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
9 | |
10 | #define MODULE_NAME "spca1528" |
11 | |
12 | #include "gspca.h" |
13 | #include "jpeg.h" |
14 | |
15 | MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>" ); |
16 | MODULE_DESCRIPTION("SPCA1528 USB Camera Driver" ); |
17 | MODULE_LICENSE("GPL" ); |
18 | |
19 | /* specific webcam descriptor */ |
20 | struct sd { |
21 | struct gspca_dev gspca_dev; /* !! must be the first item */ |
22 | |
23 | u8 pkt_seq; |
24 | |
25 | u8 jpeg_hdr[JPEG_HDR_SZ]; |
26 | }; |
27 | |
28 | static const struct v4l2_pix_format vga_mode[] = { |
29 | /* (does not work correctly) |
30 | {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
31 | .bytesperline = 176, |
32 | .sizeimage = 176 * 144 * 5 / 8 + 590, |
33 | .colorspace = V4L2_COLORSPACE_JPEG, |
34 | .priv = 3}, |
35 | */ |
36 | {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
37 | .bytesperline = 320, |
38 | .sizeimage = 320 * 240 * 4 / 8 + 590, |
39 | .colorspace = V4L2_COLORSPACE_JPEG, |
40 | .priv = 2}, |
41 | {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, |
42 | .bytesperline = 640, |
43 | .sizeimage = 640 * 480 * 3 / 8 + 590, |
44 | .colorspace = V4L2_COLORSPACE_JPEG, |
45 | .priv = 1}, |
46 | }; |
47 | |
48 | /* read <len> bytes to gspca usb_buf */ |
49 | static void reg_r(struct gspca_dev *gspca_dev, |
50 | u8 req, |
51 | u16 index, |
52 | int len) |
53 | { |
54 | #if USB_BUF_SZ < 64 |
55 | #error "USB buffer too small" |
56 | #endif |
57 | struct usb_device *dev = gspca_dev->dev; |
58 | int ret; |
59 | |
60 | if (gspca_dev->usb_err < 0) |
61 | return; |
62 | ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), |
63 | request: req, |
64 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
65 | value: 0x0000, /* value */ |
66 | index, |
67 | data: gspca_dev->usb_buf, size: len, |
68 | timeout: 500); |
69 | gspca_dbg(gspca_dev, D_USBI, "GET %02x 0000 %04x %02x\n" , req, index, |
70 | gspca_dev->usb_buf[0]); |
71 | if (ret < 0) { |
72 | pr_err("reg_r err %d\n" , ret); |
73 | gspca_dev->usb_err = ret; |
74 | /* |
75 | * Make sure the buffer is zeroed to avoid uninitialized |
76 | * values. |
77 | */ |
78 | memset(gspca_dev->usb_buf, 0, USB_BUF_SZ); |
79 | } |
80 | } |
81 | |
82 | static void reg_w(struct gspca_dev *gspca_dev, |
83 | u8 req, |
84 | u16 value, |
85 | u16 index) |
86 | { |
87 | struct usb_device *dev = gspca_dev->dev; |
88 | int ret; |
89 | |
90 | if (gspca_dev->usb_err < 0) |
91 | return; |
92 | gspca_dbg(gspca_dev, D_USBO, "SET %02x %04x %04x\n" , req, value, index); |
93 | ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), |
94 | request: req, |
95 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
96 | value, index, |
97 | NULL, size: 0, timeout: 500); |
98 | if (ret < 0) { |
99 | pr_err("reg_w err %d\n" , ret); |
100 | gspca_dev->usb_err = ret; |
101 | } |
102 | } |
103 | |
104 | static void reg_wb(struct gspca_dev *gspca_dev, |
105 | u8 req, |
106 | u16 value, |
107 | u16 index, |
108 | u8 byte) |
109 | { |
110 | struct usb_device *dev = gspca_dev->dev; |
111 | int ret; |
112 | |
113 | if (gspca_dev->usb_err < 0) |
114 | return; |
115 | gspca_dbg(gspca_dev, D_USBO, "SET %02x %04x %04x %02x\n" , |
116 | req, value, index, byte); |
117 | gspca_dev->usb_buf[0] = byte; |
118 | ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), |
119 | request: req, |
120 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, |
121 | value, index, |
122 | data: gspca_dev->usb_buf, size: 1, timeout: 500); |
123 | if (ret < 0) { |
124 | pr_err("reg_w err %d\n" , ret); |
125 | gspca_dev->usb_err = ret; |
126 | } |
127 | } |
128 | |
129 | static void wait_status_0(struct gspca_dev *gspca_dev) |
130 | { |
131 | int i, w; |
132 | |
133 | i = 16; |
134 | w = 0; |
135 | do { |
136 | reg_r(gspca_dev, req: 0x21, index: 0x0000, len: 1); |
137 | if (gspca_dev->usb_buf[0] == 0) |
138 | return; |
139 | w += 15; |
140 | msleep(msecs: w); |
141 | } while (--i > 0); |
142 | gspca_err(gspca_dev, "wait_status_0 timeout\n" ); |
143 | gspca_dev->usb_err = -ETIME; |
144 | } |
145 | |
146 | static void wait_status_1(struct gspca_dev *gspca_dev) |
147 | { |
148 | int i; |
149 | |
150 | i = 10; |
151 | do { |
152 | reg_r(gspca_dev, req: 0x21, index: 0x0001, len: 1); |
153 | msleep(msecs: 10); |
154 | if (gspca_dev->usb_buf[0] == 1) { |
155 | reg_wb(gspca_dev, req: 0x21, value: 0x0000, index: 0x0001, byte: 0x00); |
156 | reg_r(gspca_dev, req: 0x21, index: 0x0001, len: 1); |
157 | return; |
158 | } |
159 | } while (--i > 0); |
160 | gspca_err(gspca_dev, "wait_status_1 timeout\n" ); |
161 | gspca_dev->usb_err = -ETIME; |
162 | } |
163 | |
164 | static void setbrightness(struct gspca_dev *gspca_dev, s32 val) |
165 | { |
166 | reg_wb(gspca_dev, req: 0xc0, value: 0x0000, index: 0x00c0, byte: val); |
167 | } |
168 | |
169 | static void setcontrast(struct gspca_dev *gspca_dev, s32 val) |
170 | { |
171 | reg_wb(gspca_dev, req: 0xc1, value: 0x0000, index: 0x00c1, byte: val); |
172 | } |
173 | |
174 | static void sethue(struct gspca_dev *gspca_dev, s32 val) |
175 | { |
176 | reg_wb(gspca_dev, req: 0xc2, value: 0x0000, index: 0x0000, byte: val); |
177 | } |
178 | |
179 | static void setcolor(struct gspca_dev *gspca_dev, s32 val) |
180 | { |
181 | reg_wb(gspca_dev, req: 0xc3, value: 0x0000, index: 0x00c3, byte: val); |
182 | } |
183 | |
184 | static void setsharpness(struct gspca_dev *gspca_dev, s32 val) |
185 | { |
186 | reg_wb(gspca_dev, req: 0xc4, value: 0x0000, index: 0x00c4, byte: val); |
187 | } |
188 | |
189 | /* this function is called at probe time */ |
190 | static int sd_config(struct gspca_dev *gspca_dev, |
191 | const struct usb_device_id *id) |
192 | { |
193 | gspca_dev->cam.cam_mode = vga_mode; |
194 | gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); |
195 | gspca_dev->cam.npkt = 128; /* number of packets per ISOC message */ |
196 | /*fixme: 256 in ms-win traces*/ |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | /* this function is called at probe and resume time */ |
202 | static int sd_init(struct gspca_dev *gspca_dev) |
203 | { |
204 | reg_w(gspca_dev, req: 0x00, value: 0x0001, index: 0x2067); |
205 | reg_w(gspca_dev, req: 0x00, value: 0x00d0, index: 0x206b); |
206 | reg_w(gspca_dev, req: 0x00, value: 0x0000, index: 0x206c); |
207 | reg_w(gspca_dev, req: 0x00, value: 0x0001, index: 0x2069); |
208 | msleep(msecs: 8); |
209 | reg_w(gspca_dev, req: 0x00, value: 0x00c0, index: 0x206b); |
210 | reg_w(gspca_dev, req: 0x00, value: 0x0000, index: 0x206c); |
211 | reg_w(gspca_dev, req: 0x00, value: 0x0001, index: 0x2069); |
212 | |
213 | reg_r(gspca_dev, req: 0x20, index: 0x0000, len: 1); |
214 | reg_r(gspca_dev, req: 0x20, index: 0x0000, len: 5); |
215 | reg_r(gspca_dev, req: 0x23, index: 0x0000, len: 64); |
216 | gspca_dbg(gspca_dev, D_PROBE, "%s%s\n" , &gspca_dev->usb_buf[0x1c], |
217 | &gspca_dev->usb_buf[0x30]); |
218 | reg_r(gspca_dev, req: 0x23, index: 0x0001, len: 64); |
219 | return gspca_dev->usb_err; |
220 | } |
221 | |
222 | /* function called at start time before URB creation */ |
223 | static int sd_isoc_init(struct gspca_dev *gspca_dev) |
224 | { |
225 | u8 mode; |
226 | |
227 | reg_r(gspca_dev, req: 0x00, index: 0x2520, len: 1); |
228 | wait_status_0(gspca_dev); |
229 | reg_w(gspca_dev, req: 0xc5, value: 0x0003, index: 0x0000); |
230 | wait_status_1(gspca_dev); |
231 | |
232 | wait_status_0(gspca_dev); |
233 | mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; |
234 | reg_wb(gspca_dev, req: 0x25, value: 0x0000, index: 0x0004, byte: mode); |
235 | reg_r(gspca_dev, req: 0x25, index: 0x0004, len: 1); |
236 | reg_wb(gspca_dev, req: 0x27, value: 0x0000, index: 0x0000, byte: 0x06); /* 420 */ |
237 | reg_r(gspca_dev, req: 0x27, index: 0x0000, len: 1); |
238 | |
239 | /* not useful.. |
240 | gspca_dev->alt = 4; * use alternate setting 3 */ |
241 | |
242 | return gspca_dev->usb_err; |
243 | } |
244 | |
245 | /* -- start the camera -- */ |
246 | static int sd_start(struct gspca_dev *gspca_dev) |
247 | { |
248 | struct sd *sd = (struct sd *) gspca_dev; |
249 | |
250 | /* initialize the JPEG header */ |
251 | jpeg_define(jpeg_hdr: sd->jpeg_hdr, height: gspca_dev->pixfmt.height, |
252 | width: gspca_dev->pixfmt.width, |
253 | samplesY: 0x22); /* JPEG 411 */ |
254 | |
255 | /* the JPEG quality shall be 85% */ |
256 | jpeg_set_qual(jpeg_hdr: sd->jpeg_hdr, quality: 85); |
257 | |
258 | reg_r(gspca_dev, req: 0x00, index: 0x2520, len: 1); |
259 | msleep(msecs: 8); |
260 | |
261 | /* start the capture */ |
262 | wait_status_0(gspca_dev); |
263 | reg_w(gspca_dev, req: 0x31, value: 0x0000, index: 0x0004); /* start request */ |
264 | wait_status_1(gspca_dev); |
265 | wait_status_0(gspca_dev); |
266 | msleep(msecs: 200); |
267 | |
268 | sd->pkt_seq = 0; |
269 | return gspca_dev->usb_err; |
270 | } |
271 | |
272 | static void sd_stopN(struct gspca_dev *gspca_dev) |
273 | { |
274 | /* stop the capture */ |
275 | wait_status_0(gspca_dev); |
276 | reg_w(gspca_dev, req: 0x31, value: 0x0000, index: 0x0000); /* stop request */ |
277 | wait_status_1(gspca_dev); |
278 | wait_status_0(gspca_dev); |
279 | } |
280 | |
281 | /* move a packet adding 0x00 after 0xff */ |
282 | static void add_packet(struct gspca_dev *gspca_dev, |
283 | u8 *data, |
284 | int len) |
285 | { |
286 | int i; |
287 | |
288 | i = 0; |
289 | do { |
290 | if (data[i] == 0xff) { |
291 | gspca_frame_add(gspca_dev, packet_type: INTER_PACKET, |
292 | data, len: i + 1); |
293 | len -= i; |
294 | data += i; |
295 | *data = 0x00; |
296 | i = 0; |
297 | } |
298 | } while (++i < len); |
299 | gspca_frame_add(gspca_dev, packet_type: INTER_PACKET, data, len); |
300 | } |
301 | |
302 | static void sd_pkt_scan(struct gspca_dev *gspca_dev, |
303 | u8 *data, /* isoc packet */ |
304 | int len) /* iso packet length */ |
305 | { |
306 | struct sd *sd = (struct sd *) gspca_dev; |
307 | static const u8 ffd9[] = {0xff, 0xd9}; |
308 | |
309 | /* image packets start with: |
310 | * 02 8n |
311 | * with <n> bit: |
312 | * 0x01: even (0) / odd (1) image |
313 | * 0x02: end of image when set |
314 | */ |
315 | if (len < 3) |
316 | return; /* empty packet */ |
317 | if (*data == 0x02) { |
318 | if (data[1] & 0x02) { |
319 | sd->pkt_seq = !(data[1] & 1); |
320 | add_packet(gspca_dev, data: data + 2, len: len - 2); |
321 | gspca_frame_add(gspca_dev, packet_type: LAST_PACKET, |
322 | data: ffd9, len: 2); |
323 | return; |
324 | } |
325 | if ((data[1] & 1) != sd->pkt_seq) |
326 | goto err; |
327 | if (gspca_dev->last_packet_type == LAST_PACKET) |
328 | gspca_frame_add(gspca_dev, packet_type: FIRST_PACKET, |
329 | data: sd->jpeg_hdr, JPEG_HDR_SZ); |
330 | add_packet(gspca_dev, data: data + 2, len: len - 2); |
331 | return; |
332 | } |
333 | err: |
334 | gspca_dev->last_packet_type = DISCARD_PACKET; |
335 | } |
336 | |
337 | static int sd_s_ctrl(struct v4l2_ctrl *ctrl) |
338 | { |
339 | struct gspca_dev *gspca_dev = |
340 | container_of(ctrl->handler, struct gspca_dev, ctrl_handler); |
341 | |
342 | gspca_dev->usb_err = 0; |
343 | |
344 | if (!gspca_dev->streaming) |
345 | return 0; |
346 | |
347 | switch (ctrl->id) { |
348 | case V4L2_CID_BRIGHTNESS: |
349 | setbrightness(gspca_dev, val: ctrl->val); |
350 | break; |
351 | case V4L2_CID_CONTRAST: |
352 | setcontrast(gspca_dev, val: ctrl->val); |
353 | break; |
354 | case V4L2_CID_HUE: |
355 | sethue(gspca_dev, val: ctrl->val); |
356 | break; |
357 | case V4L2_CID_SATURATION: |
358 | setcolor(gspca_dev, val: ctrl->val); |
359 | break; |
360 | case V4L2_CID_SHARPNESS: |
361 | setsharpness(gspca_dev, val: ctrl->val); |
362 | break; |
363 | } |
364 | return gspca_dev->usb_err; |
365 | } |
366 | |
367 | static const struct v4l2_ctrl_ops sd_ctrl_ops = { |
368 | .s_ctrl = sd_s_ctrl, |
369 | }; |
370 | |
371 | static int sd_init_controls(struct gspca_dev *gspca_dev) |
372 | { |
373 | struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; |
374 | |
375 | gspca_dev->vdev.ctrl_handler = hdl; |
376 | v4l2_ctrl_handler_init(hdl, 5); |
377 | v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops, |
378 | V4L2_CID_BRIGHTNESS, min: 0, max: 255, step: 1, def: 128); |
379 | v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops, |
380 | V4L2_CID_CONTRAST, min: 0, max: 8, step: 1, def: 1); |
381 | v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops, |
382 | V4L2_CID_HUE, min: 0, max: 255, step: 1, def: 0); |
383 | v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops, |
384 | V4L2_CID_SATURATION, min: 0, max: 8, step: 1, def: 1); |
385 | v4l2_ctrl_new_std(hdl, ops: &sd_ctrl_ops, |
386 | V4L2_CID_SHARPNESS, min: 0, max: 255, step: 1, def: 0); |
387 | |
388 | if (hdl->error) { |
389 | pr_err("Could not initialize controls\n" ); |
390 | return hdl->error; |
391 | } |
392 | return 0; |
393 | } |
394 | |
395 | /* sub-driver description */ |
396 | static const struct sd_desc sd_desc = { |
397 | .name = MODULE_NAME, |
398 | .config = sd_config, |
399 | .init = sd_init, |
400 | .init_controls = sd_init_controls, |
401 | .isoc_init = sd_isoc_init, |
402 | .start = sd_start, |
403 | .stopN = sd_stopN, |
404 | .pkt_scan = sd_pkt_scan, |
405 | }; |
406 | |
407 | /* -- module initialisation -- */ |
408 | static const struct usb_device_id device_table[] = { |
409 | {USB_DEVICE(0x04fc, 0x1528)}, |
410 | {} |
411 | }; |
412 | MODULE_DEVICE_TABLE(usb, device_table); |
413 | |
414 | /* -- device connect -- */ |
415 | static int sd_probe(struct usb_interface *intf, |
416 | const struct usb_device_id *id) |
417 | { |
418 | /* the video interface for isochronous transfer is 1 */ |
419 | if (intf->cur_altsetting->desc.bInterfaceNumber != 1) |
420 | return -ENODEV; |
421 | |
422 | return gspca_dev_probe2(intf, id, sd_desc: &sd_desc, dev_size: sizeof(struct sd), |
423 | THIS_MODULE); |
424 | } |
425 | |
426 | static struct usb_driver sd_driver = { |
427 | .name = MODULE_NAME, |
428 | .id_table = device_table, |
429 | .probe = sd_probe, |
430 | .disconnect = gspca_disconnect, |
431 | #ifdef CONFIG_PM |
432 | .suspend = gspca_suspend, |
433 | .resume = gspca_resume, |
434 | .reset_resume = gspca_resume, |
435 | #endif |
436 | }; |
437 | |
438 | module_usb_driver(sd_driver); |
439 | |