Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/media/video/gspca/pac7302.c
17633 views
1
/*
2
* Pixart PAC7302 library
3
* Copyright (C) 2005 Thomas Kaiser [email protected]
4
*
5
* V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
6
*
7
* Separated from Pixart PAC7311 library by Márton Németh
8
* Camera button input handling by Márton Németh <[email protected]>
9
* Copyright (C) 2009-2010 Márton Németh <[email protected]>
10
*
11
* This program is free software; you can redistribute it and/or modify
12
* it under the terms of the GNU General Public License as published by
13
* the Free Software Foundation; either version 2 of the License, or
14
* any later version.
15
*
16
* This program is distributed in the hope that it will be useful,
17
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
* GNU General Public License for more details.
20
*
21
* You should have received a copy of the GNU General Public License
22
* along with this program; if not, write to the Free Software
23
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24
*/
25
26
/* Some documentation about various registers as determined by trial and error.
27
28
Register page 1:
29
30
Address Description
31
0x78 Global control, bit 6 controls the LED (inverted)
32
33
Register page 3:
34
35
Address Description
36
0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on
37
the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
38
0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps
39
0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps,
40
63 -> ~27 fps, the 2 msb's must always be 1 !!
41
0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0:
42
1 -> ~30 fps, 2 -> ~20 fps
43
0x0e Exposure bits 0-7, 0-448, 0 = use full frame time
44
0x0f Exposure bit 8, 0-448, 448 = no exposure at all
45
0x10 Master gain 0-31
46
0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
47
48
The registers are accessed in the following functions:
49
50
Page | Register | Function
51
-----+------------+---------------------------------------------------
52
0 | 0x0f..0x20 | setcolors()
53
0 | 0xa2..0xab | setbrightcont()
54
0 | 0xc5 | setredbalance()
55
0 | 0xc6 | setwhitebalance()
56
0 | 0xc7 | setbluebalance()
57
0 | 0xdc | setbrightcont(), setcolors()
58
3 | 0x02 | setexposure()
59
3 | 0x10 | setgain()
60
3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip()
61
3 | 0x21 | sethvflip()
62
*/
63
64
#define MODULE_NAME "pac7302"
65
66
#include <linux/input.h>
67
#include <media/v4l2-chip-ident.h>
68
#include "gspca.h"
69
70
MODULE_AUTHOR("Thomas Kaiser [email protected]");
71
MODULE_DESCRIPTION("Pixart PAC7302");
72
MODULE_LICENSE("GPL");
73
74
/* specific webcam descriptor for pac7302 */
75
struct sd {
76
struct gspca_dev gspca_dev; /* !! must be the first item */
77
78
unsigned char brightness;
79
unsigned char contrast;
80
unsigned char colors;
81
unsigned char white_balance;
82
unsigned char red_balance;
83
unsigned char blue_balance;
84
unsigned char gain;
85
unsigned char autogain;
86
unsigned short exposure;
87
__u8 hflip;
88
__u8 vflip;
89
u8 flags;
90
#define FL_HFLIP 0x01 /* mirrored by default */
91
#define FL_VFLIP 0x02 /* vertical flipped by default */
92
93
u8 sof_read;
94
u8 autogain_ignore_frames;
95
96
atomic_t avg_lum;
97
};
98
99
/* V4L2 controls supported by the driver */
100
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
101
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
102
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
103
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
104
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
105
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
106
static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val);
107
static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val);
108
static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val);
109
static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val);
110
static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val);
111
static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val);
112
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
113
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
114
static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
115
static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
116
static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
117
static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
118
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
119
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
120
static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
121
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
122
123
static const struct ctrl sd_ctrls[] = {
124
{
125
{
126
.id = V4L2_CID_BRIGHTNESS,
127
.type = V4L2_CTRL_TYPE_INTEGER,
128
.name = "Brightness",
129
.minimum = 0,
130
#define BRIGHTNESS_MAX 0x20
131
.maximum = BRIGHTNESS_MAX,
132
.step = 1,
133
#define BRIGHTNESS_DEF 0x10
134
.default_value = BRIGHTNESS_DEF,
135
},
136
.set = sd_setbrightness,
137
.get = sd_getbrightness,
138
},
139
{
140
{
141
.id = V4L2_CID_CONTRAST,
142
.type = V4L2_CTRL_TYPE_INTEGER,
143
.name = "Contrast",
144
.minimum = 0,
145
#define CONTRAST_MAX 255
146
.maximum = CONTRAST_MAX,
147
.step = 1,
148
#define CONTRAST_DEF 127
149
.default_value = CONTRAST_DEF,
150
},
151
.set = sd_setcontrast,
152
.get = sd_getcontrast,
153
},
154
{
155
{
156
.id = V4L2_CID_SATURATION,
157
.type = V4L2_CTRL_TYPE_INTEGER,
158
.name = "Saturation",
159
.minimum = 0,
160
#define COLOR_MAX 255
161
.maximum = COLOR_MAX,
162
.step = 1,
163
#define COLOR_DEF 127
164
.default_value = COLOR_DEF,
165
},
166
.set = sd_setcolors,
167
.get = sd_getcolors,
168
},
169
{
170
{
171
.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
172
.type = V4L2_CTRL_TYPE_INTEGER,
173
.name = "White Balance",
174
.minimum = 0,
175
.maximum = 255,
176
.step = 1,
177
#define WHITEBALANCE_DEF 4
178
.default_value = WHITEBALANCE_DEF,
179
},
180
.set = sd_setwhitebalance,
181
.get = sd_getwhitebalance,
182
},
183
{
184
{
185
.id = V4L2_CID_RED_BALANCE,
186
.type = V4L2_CTRL_TYPE_INTEGER,
187
.name = "Red",
188
.minimum = 0,
189
.maximum = 3,
190
.step = 1,
191
#define REDBALANCE_DEF 1
192
.default_value = REDBALANCE_DEF,
193
},
194
.set = sd_setredbalance,
195
.get = sd_getredbalance,
196
},
197
{
198
{
199
.id = V4L2_CID_BLUE_BALANCE,
200
.type = V4L2_CTRL_TYPE_INTEGER,
201
.name = "Blue",
202
.minimum = 0,
203
.maximum = 3,
204
.step = 1,
205
#define BLUEBALANCE_DEF 1
206
.default_value = BLUEBALANCE_DEF,
207
},
208
.set = sd_setbluebalance,
209
.get = sd_getbluebalance,
210
},
211
{
212
{
213
.id = V4L2_CID_GAIN,
214
.type = V4L2_CTRL_TYPE_INTEGER,
215
.name = "Gain",
216
.minimum = 0,
217
#define GAIN_MAX 255
218
.maximum = GAIN_MAX,
219
.step = 1,
220
#define GAIN_DEF 127
221
#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */
222
.default_value = GAIN_DEF,
223
},
224
.set = sd_setgain,
225
.get = sd_getgain,
226
},
227
{
228
{
229
.id = V4L2_CID_EXPOSURE,
230
.type = V4L2_CTRL_TYPE_INTEGER,
231
.name = "Exposure",
232
.minimum = 0,
233
.maximum = 1023,
234
.step = 1,
235
#define EXPOSURE_DEF 66 /* 33 ms / 30 fps */
236
#define EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
237
.default_value = EXPOSURE_DEF,
238
},
239
.set = sd_setexposure,
240
.get = sd_getexposure,
241
},
242
{
243
{
244
.id = V4L2_CID_AUTOGAIN,
245
.type = V4L2_CTRL_TYPE_BOOLEAN,
246
.name = "Auto Gain",
247
.minimum = 0,
248
.maximum = 1,
249
.step = 1,
250
#define AUTOGAIN_DEF 1
251
.default_value = AUTOGAIN_DEF,
252
},
253
.set = sd_setautogain,
254
.get = sd_getautogain,
255
},
256
{
257
{
258
.id = V4L2_CID_HFLIP,
259
.type = V4L2_CTRL_TYPE_BOOLEAN,
260
.name = "Mirror",
261
.minimum = 0,
262
.maximum = 1,
263
.step = 1,
264
#define HFLIP_DEF 0
265
.default_value = HFLIP_DEF,
266
},
267
.set = sd_sethflip,
268
.get = sd_gethflip,
269
},
270
{
271
{
272
.id = V4L2_CID_VFLIP,
273
.type = V4L2_CTRL_TYPE_BOOLEAN,
274
.name = "Vflip",
275
.minimum = 0,
276
.maximum = 1,
277
.step = 1,
278
#define VFLIP_DEF 0
279
.default_value = VFLIP_DEF,
280
},
281
.set = sd_setvflip,
282
.get = sd_getvflip,
283
},
284
};
285
286
static const struct v4l2_pix_format vga_mode[] = {
287
{640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
288
.bytesperline = 640,
289
.sizeimage = 640 * 480 * 3 / 8 + 590,
290
.colorspace = V4L2_COLORSPACE_JPEG,
291
.priv = 0},
292
};
293
294
#define LOAD_PAGE3 255
295
#define END_OF_SEQUENCE 0
296
297
/* pac 7302 */
298
static const __u8 init_7302[] = {
299
/* index,value */
300
0xff, 0x01, /* page 1 */
301
0x78, 0x00, /* deactivate */
302
0xff, 0x01,
303
0x78, 0x40, /* led off */
304
};
305
static const __u8 start_7302[] = {
306
/* index, len, [value]* */
307
0xff, 1, 0x00, /* page 0 */
308
0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
309
0x00, 0x00, 0x00, 0x00,
310
0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00,
311
0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7,
312
0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11,
313
0x26, 2, 0xaa, 0xaa,
314
0x2e, 1, 0x31,
315
0x38, 1, 0x01,
316
0x3a, 3, 0x14, 0xff, 0x5a,
317
0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
318
0x00, 0x54, 0x11,
319
0x55, 1, 0x00,
320
0x62, 4, 0x10, 0x1e, 0x1e, 0x18,
321
0x6b, 1, 0x00,
322
0x6e, 3, 0x08, 0x06, 0x00,
323
0x72, 3, 0x00, 0xff, 0x00,
324
0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c,
325
0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50,
326
0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00,
327
0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9,
328
0xd2, 0xeb,
329
0xaf, 1, 0x02,
330
0xb5, 2, 0x08, 0x08,
331
0xb8, 2, 0x08, 0x88,
332
0xc4, 4, 0xae, 0x01, 0x04, 0x01,
333
0xcc, 1, 0x00,
334
0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9,
335
0xc1, 0xd7, 0xec,
336
0xdc, 1, 0x01,
337
0xff, 1, 0x01, /* page 1 */
338
0x12, 3, 0x02, 0x00, 0x01,
339
0x3e, 2, 0x00, 0x00,
340
0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2,
341
0x7c, 1, 0x00,
342
0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20,
343
0x02, 0x00,
344
0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04,
345
0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
346
0x07, 0x00, 0x01, 0x07, 0x04, 0x01,
347
0xd8, 1, 0x01,
348
0xdb, 2, 0x00, 0x01,
349
0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00,
350
0xe6, 4, 0x00, 0x00, 0x00, 0x01,
351
0xeb, 1, 0x00,
352
0xff, 1, 0x02, /* page 2 */
353
0x22, 1, 0x00,
354
0xff, 1, 0x03, /* page 3 */
355
0, LOAD_PAGE3, /* load the page 3 */
356
0x11, 1, 0x01,
357
0xff, 1, 0x02, /* page 2 */
358
0x13, 1, 0x00,
359
0x22, 4, 0x1f, 0xa4, 0xf0, 0x96,
360
0x27, 2, 0x14, 0x0c,
361
0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22,
362
0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
363
0x6e, 1, 0x08,
364
0xff, 1, 0x01, /* page 1 */
365
0x78, 1, 0x00,
366
0, END_OF_SEQUENCE /* end of sequence */
367
};
368
369
#define SKIP 0xaa
370
/* page 3 - the value SKIP says skip the index - see reg_w_page() */
371
static const __u8 page3_7302[] = {
372
0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16,
373
0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
374
0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
375
0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00,
376
0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21,
377
0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54,
378
0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00,
379
0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
380
0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00,
381
SKIP, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00,
382
0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
383
0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00,
384
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
385
0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8,
386
0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
387
0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00,
388
0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00,
389
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
390
0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00,
391
0x00
392
};
393
394
static void reg_w_buf(struct gspca_dev *gspca_dev,
395
__u8 index,
396
const u8 *buffer, int len)
397
{
398
int ret;
399
400
if (gspca_dev->usb_err < 0)
401
return;
402
memcpy(gspca_dev->usb_buf, buffer, len);
403
ret = usb_control_msg(gspca_dev->dev,
404
usb_sndctrlpipe(gspca_dev->dev, 0),
405
0, /* request */
406
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
407
0, /* value */
408
index, gspca_dev->usb_buf, len,
409
500);
410
if (ret < 0) {
411
err("reg_w_buf failed index 0x%02x, error %d",
412
index, ret);
413
gspca_dev->usb_err = ret;
414
}
415
}
416
417
418
static void reg_w(struct gspca_dev *gspca_dev,
419
__u8 index,
420
__u8 value)
421
{
422
int ret;
423
424
if (gspca_dev->usb_err < 0)
425
return;
426
gspca_dev->usb_buf[0] = value;
427
ret = usb_control_msg(gspca_dev->dev,
428
usb_sndctrlpipe(gspca_dev->dev, 0),
429
0, /* request */
430
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
431
0, index, gspca_dev->usb_buf, 1,
432
500);
433
if (ret < 0) {
434
err("reg_w() failed index 0x%02x, value 0x%02x, error %d",
435
index, value, ret);
436
gspca_dev->usb_err = ret;
437
}
438
}
439
440
static void reg_w_seq(struct gspca_dev *gspca_dev,
441
const __u8 *seq, int len)
442
{
443
while (--len >= 0) {
444
reg_w(gspca_dev, seq[0], seq[1]);
445
seq += 2;
446
}
447
}
448
449
/* load the beginning of a page */
450
static void reg_w_page(struct gspca_dev *gspca_dev,
451
const __u8 *page, int len)
452
{
453
int index;
454
int ret = 0;
455
456
if (gspca_dev->usb_err < 0)
457
return;
458
for (index = 0; index < len; index++) {
459
if (page[index] == SKIP) /* skip this index */
460
continue;
461
gspca_dev->usb_buf[0] = page[index];
462
ret = usb_control_msg(gspca_dev->dev,
463
usb_sndctrlpipe(gspca_dev->dev, 0),
464
0, /* request */
465
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
466
0, index, gspca_dev->usb_buf, 1,
467
500);
468
if (ret < 0) {
469
err("reg_w_page() failed index 0x%02x, "
470
"value 0x%02x, error %d",
471
index, page[index], ret);
472
gspca_dev->usb_err = ret;
473
break;
474
}
475
}
476
}
477
478
/* output a variable sequence */
479
static void reg_w_var(struct gspca_dev *gspca_dev,
480
const __u8 *seq,
481
const __u8 *page3, unsigned int page3_len)
482
{
483
int index, len;
484
485
for (;;) {
486
index = *seq++;
487
len = *seq++;
488
switch (len) {
489
case END_OF_SEQUENCE:
490
return;
491
case LOAD_PAGE3:
492
reg_w_page(gspca_dev, page3, page3_len);
493
break;
494
default:
495
if (len > USB_BUF_SZ) {
496
PDEBUG(D_ERR|D_STREAM,
497
"Incorrect variable sequence");
498
return;
499
}
500
while (len > 0) {
501
if (len < 8) {
502
reg_w_buf(gspca_dev,
503
index, seq, len);
504
seq += len;
505
break;
506
}
507
reg_w_buf(gspca_dev, index, seq, 8);
508
seq += 8;
509
index += 8;
510
len -= 8;
511
}
512
}
513
}
514
/* not reached */
515
}
516
517
/* this function is called at probe time for pac7302 */
518
static int sd_config(struct gspca_dev *gspca_dev,
519
const struct usb_device_id *id)
520
{
521
struct sd *sd = (struct sd *) gspca_dev;
522
struct cam *cam;
523
524
cam = &gspca_dev->cam;
525
526
PDEBUG(D_CONF, "Find Sensor PAC7302");
527
cam->cam_mode = vga_mode; /* only 640x480 */
528
cam->nmodes = ARRAY_SIZE(vga_mode);
529
530
sd->brightness = BRIGHTNESS_DEF;
531
sd->contrast = CONTRAST_DEF;
532
sd->colors = COLOR_DEF;
533
sd->white_balance = WHITEBALANCE_DEF;
534
sd->red_balance = REDBALANCE_DEF;
535
sd->blue_balance = BLUEBALANCE_DEF;
536
sd->gain = GAIN_DEF;
537
sd->exposure = EXPOSURE_DEF;
538
sd->autogain = AUTOGAIN_DEF;
539
sd->hflip = HFLIP_DEF;
540
sd->vflip = VFLIP_DEF;
541
sd->flags = id->driver_info;
542
return 0;
543
}
544
545
/* This function is used by pac7302 only */
546
static void setbrightcont(struct gspca_dev *gspca_dev)
547
{
548
struct sd *sd = (struct sd *) gspca_dev;
549
int i, v;
550
static const __u8 max[10] =
551
{0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
552
0xd4, 0xec};
553
static const __u8 delta[10] =
554
{0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
555
0x11, 0x0b};
556
557
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
558
for (i = 0; i < 10; i++) {
559
v = max[i];
560
v += (sd->brightness - BRIGHTNESS_MAX)
561
* 150 / BRIGHTNESS_MAX; /* 200 ? */
562
v -= delta[i] * sd->contrast / CONTRAST_MAX;
563
if (v < 0)
564
v = 0;
565
else if (v > 0xff)
566
v = 0xff;
567
reg_w(gspca_dev, 0xa2 + i, v);
568
}
569
reg_w(gspca_dev, 0xdc, 0x01);
570
}
571
572
/* This function is used by pac7302 only */
573
static void setcolors(struct gspca_dev *gspca_dev)
574
{
575
struct sd *sd = (struct sd *) gspca_dev;
576
int i, v;
577
static const int a[9] =
578
{217, -212, 0, -101, 170, -67, -38, -315, 355};
579
static const int b[9] =
580
{19, 106, 0, 19, 106, 1, 19, 106, 1};
581
582
reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
583
reg_w(gspca_dev, 0x11, 0x01);
584
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
585
for (i = 0; i < 9; i++) {
586
v = a[i] * sd->colors / COLOR_MAX + b[i];
587
reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
588
reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
589
}
590
reg_w(gspca_dev, 0xdc, 0x01);
591
PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
592
}
593
594
static void setwhitebalance(struct gspca_dev *gspca_dev)
595
{
596
struct sd *sd = (struct sd *) gspca_dev;
597
598
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
599
reg_w(gspca_dev, 0xc6, sd->white_balance);
600
601
reg_w(gspca_dev, 0xdc, 0x01);
602
PDEBUG(D_CONF|D_STREAM, "white_balance: %i", sd->white_balance);
603
}
604
605
static void setredbalance(struct gspca_dev *gspca_dev)
606
{
607
struct sd *sd = (struct sd *) gspca_dev;
608
609
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
610
reg_w(gspca_dev, 0xc5, sd->red_balance);
611
612
reg_w(gspca_dev, 0xdc, 0x01);
613
PDEBUG(D_CONF|D_STREAM, "red_balance: %i", sd->red_balance);
614
}
615
616
static void setbluebalance(struct gspca_dev *gspca_dev)
617
{
618
struct sd *sd = (struct sd *) gspca_dev;
619
620
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
621
reg_w(gspca_dev, 0xc7, sd->blue_balance);
622
623
reg_w(gspca_dev, 0xdc, 0x01);
624
PDEBUG(D_CONF|D_STREAM, "blue_balance: %i", sd->blue_balance);
625
}
626
627
static void setgain(struct gspca_dev *gspca_dev)
628
{
629
struct sd *sd = (struct sd *) gspca_dev;
630
631
reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
632
reg_w(gspca_dev, 0x10, sd->gain >> 3);
633
634
/* load registers to sensor (Bit 0, auto clear) */
635
reg_w(gspca_dev, 0x11, 0x01);
636
}
637
638
static void setexposure(struct gspca_dev *gspca_dev)
639
{
640
struct sd *sd = (struct sd *) gspca_dev;
641
__u8 clockdiv;
642
__u16 exposure;
643
644
/* register 2 of frame 3 contains the clock divider configuring the
645
no fps according to the formula: 90 / reg. sd->exposure is the
646
desired exposure time in 0.5 ms. */
647
clockdiv = (90 * sd->exposure + 1999) / 2000;
648
649
/* Note clockdiv = 3 also works, but when running at 30 fps, depending
650
on the scene being recorded, the camera switches to another
651
quantization table for certain JPEG blocks, and we don't know how
652
to decompress these blocks. So we cap the framerate at 15 fps */
653
if (clockdiv < 6)
654
clockdiv = 6;
655
else if (clockdiv > 63)
656
clockdiv = 63;
657
658
/* reg2 MUST be a multiple of 3, except when between 6 and 12?
659
Always round up, otherwise we cannot get the desired frametime
660
using the partial frame time exposure control */
661
if (clockdiv < 6 || clockdiv > 12)
662
clockdiv = ((clockdiv + 2) / 3) * 3;
663
664
/* frame exposure time in ms = 1000 * clockdiv / 90 ->
665
exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */
666
exposure = (sd->exposure * 45 * 448) / (1000 * clockdiv);
667
/* 0 = use full frametime, 448 = no exposure, reverse it */
668
exposure = 448 - exposure;
669
670
reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
671
reg_w(gspca_dev, 0x02, clockdiv);
672
reg_w(gspca_dev, 0x0e, exposure & 0xff);
673
reg_w(gspca_dev, 0x0f, exposure >> 8);
674
675
/* load registers to sensor (Bit 0, auto clear) */
676
reg_w(gspca_dev, 0x11, 0x01);
677
}
678
679
static void sethvflip(struct gspca_dev *gspca_dev)
680
{
681
struct sd *sd = (struct sd *) gspca_dev;
682
u8 data, hflip, vflip;
683
684
hflip = sd->hflip;
685
if (sd->flags & FL_HFLIP)
686
hflip = !hflip;
687
vflip = sd->vflip;
688
if (sd->flags & FL_VFLIP)
689
vflip = !vflip;
690
691
reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
692
data = (hflip ? 0x08 : 0x00) | (vflip ? 0x04 : 0x00);
693
reg_w(gspca_dev, 0x21, data);
694
695
/* load registers to sensor (Bit 0, auto clear) */
696
reg_w(gspca_dev, 0x11, 0x01);
697
}
698
699
/* this function is called at probe and resume time for pac7302 */
700
static int sd_init(struct gspca_dev *gspca_dev)
701
{
702
reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2);
703
return gspca_dev->usb_err;
704
}
705
706
static int sd_start(struct gspca_dev *gspca_dev)
707
{
708
struct sd *sd = (struct sd *) gspca_dev;
709
710
sd->sof_read = 0;
711
712
reg_w_var(gspca_dev, start_7302,
713
page3_7302, sizeof(page3_7302));
714
setbrightcont(gspca_dev);
715
setcolors(gspca_dev);
716
setwhitebalance(gspca_dev);
717
setredbalance(gspca_dev);
718
setbluebalance(gspca_dev);
719
setgain(gspca_dev);
720
setexposure(gspca_dev);
721
sethvflip(gspca_dev);
722
723
/* only resolution 640x480 is supported for pac7302 */
724
725
sd->sof_read = 0;
726
sd->autogain_ignore_frames = 0;
727
atomic_set(&sd->avg_lum, -1);
728
729
/* start stream */
730
reg_w(gspca_dev, 0xff, 0x01);
731
reg_w(gspca_dev, 0x78, 0x01);
732
733
return gspca_dev->usb_err;
734
}
735
736
static void sd_stopN(struct gspca_dev *gspca_dev)
737
{
738
739
/* stop stream */
740
reg_w(gspca_dev, 0xff, 0x01);
741
reg_w(gspca_dev, 0x78, 0x00);
742
}
743
744
/* called on streamoff with alt 0 and on disconnect for pac7302 */
745
static void sd_stop0(struct gspca_dev *gspca_dev)
746
{
747
if (!gspca_dev->present)
748
return;
749
reg_w(gspca_dev, 0xff, 0x01);
750
reg_w(gspca_dev, 0x78, 0x40);
751
}
752
753
/* Include pac common sof detection functions */
754
#include "pac_common.h"
755
756
static void do_autogain(struct gspca_dev *gspca_dev)
757
{
758
struct sd *sd = (struct sd *) gspca_dev;
759
int avg_lum = atomic_read(&sd->avg_lum);
760
int desired_lum;
761
const int deadzone = 30;
762
763
if (avg_lum == -1)
764
return;
765
766
desired_lum = 270 + sd->brightness;
767
768
if (sd->autogain_ignore_frames > 0)
769
sd->autogain_ignore_frames--;
770
else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
771
deadzone, GAIN_KNEE, EXPOSURE_KNEE))
772
sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
773
}
774
775
/* JPEG header, part 1 */
776
static const unsigned char pac_jpeg_header1[] = {
777
0xff, 0xd8, /* SOI: Start of Image */
778
779
0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
780
0x00, 0x11, /* length = 17 bytes (including this length field) */
781
0x08 /* Precision: 8 */
782
/* 2 bytes is placed here: number of image lines */
783
/* 2 bytes is placed here: samples per line */
784
};
785
786
/* JPEG header, continued */
787
static const unsigned char pac_jpeg_header2[] = {
788
0x03, /* Number of image components: 3 */
789
0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
790
0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
791
0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
792
793
0xff, 0xda, /* SOS: Start Of Scan */
794
0x00, 0x0c, /* length = 12 bytes (including this length field) */
795
0x03, /* number of components: 3 */
796
0x01, 0x00, /* selector 1, table 0x00 */
797
0x02, 0x11, /* selector 2, table 0x11 */
798
0x03, 0x11, /* selector 3, table 0x11 */
799
0x00, 0x3f, /* Spectral selection: 0 .. 63 */
800
0x00 /* Successive approximation: 0 */
801
};
802
803
static void pac_start_frame(struct gspca_dev *gspca_dev,
804
__u16 lines, __u16 samples_per_line)
805
{
806
unsigned char tmpbuf[4];
807
808
gspca_frame_add(gspca_dev, FIRST_PACKET,
809
pac_jpeg_header1, sizeof(pac_jpeg_header1));
810
811
tmpbuf[0] = lines >> 8;
812
tmpbuf[1] = lines & 0xff;
813
tmpbuf[2] = samples_per_line >> 8;
814
tmpbuf[3] = samples_per_line & 0xff;
815
816
gspca_frame_add(gspca_dev, INTER_PACKET,
817
tmpbuf, sizeof(tmpbuf));
818
gspca_frame_add(gspca_dev, INTER_PACKET,
819
pac_jpeg_header2, sizeof(pac_jpeg_header2));
820
}
821
822
/* this function is run at interrupt level */
823
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
824
u8 *data, /* isoc packet */
825
int len) /* iso packet length */
826
{
827
struct sd *sd = (struct sd *) gspca_dev;
828
u8 *image;
829
unsigned char *sof;
830
831
sof = pac_find_sof(&sd->sof_read, data, len);
832
if (sof) {
833
int n, lum_offset, footer_length;
834
835
/* 6 bytes after the FF D9 EOF marker a number of lumination
836
bytes are send corresponding to different parts of the
837
image, the 14th and 15th byte after the EOF seem to
838
correspond to the center of the image */
839
lum_offset = 61 + sizeof pac_sof_marker;
840
footer_length = 74;
841
842
/* Finish decoding current frame */
843
n = (sof - data) - (footer_length + sizeof pac_sof_marker);
844
if (n < 0) {
845
gspca_dev->image_len += n;
846
n = 0;
847
} else {
848
gspca_frame_add(gspca_dev, INTER_PACKET, data, n);
849
}
850
851
image = gspca_dev->image;
852
if (image != NULL
853
&& image[gspca_dev->image_len - 2] == 0xff
854
&& image[gspca_dev->image_len - 1] == 0xd9)
855
gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
856
857
n = sof - data;
858
len -= n;
859
data = sof;
860
861
/* Get average lumination */
862
if (gspca_dev->last_packet_type == LAST_PACKET &&
863
n >= lum_offset)
864
atomic_set(&sd->avg_lum, data[-lum_offset] +
865
data[-lum_offset + 1]);
866
else
867
atomic_set(&sd->avg_lum, -1);
868
869
/* Start the new frame with the jpeg header */
870
/* The PAC7302 has the image rotated 90 degrees */
871
pac_start_frame(gspca_dev,
872
gspca_dev->width, gspca_dev->height);
873
}
874
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
875
}
876
877
static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
878
{
879
struct sd *sd = (struct sd *) gspca_dev;
880
881
sd->brightness = val;
882
if (gspca_dev->streaming)
883
setbrightcont(gspca_dev);
884
return gspca_dev->usb_err;
885
}
886
887
static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
888
{
889
struct sd *sd = (struct sd *) gspca_dev;
890
891
*val = sd->brightness;
892
return 0;
893
}
894
895
static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
896
{
897
struct sd *sd = (struct sd *) gspca_dev;
898
899
sd->contrast = val;
900
if (gspca_dev->streaming)
901
setbrightcont(gspca_dev);
902
return gspca_dev->usb_err;
903
}
904
905
static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
906
{
907
struct sd *sd = (struct sd *) gspca_dev;
908
909
*val = sd->contrast;
910
return 0;
911
}
912
913
static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
914
{
915
struct sd *sd = (struct sd *) gspca_dev;
916
917
sd->colors = val;
918
if (gspca_dev->streaming)
919
setcolors(gspca_dev);
920
return gspca_dev->usb_err;
921
}
922
923
static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
924
{
925
struct sd *sd = (struct sd *) gspca_dev;
926
927
*val = sd->colors;
928
return 0;
929
}
930
931
static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val)
932
{
933
struct sd *sd = (struct sd *) gspca_dev;
934
935
sd->white_balance = val;
936
if (gspca_dev->streaming)
937
setwhitebalance(gspca_dev);
938
return gspca_dev->usb_err;
939
}
940
941
static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val)
942
{
943
struct sd *sd = (struct sd *) gspca_dev;
944
945
*val = sd->white_balance;
946
return 0;
947
}
948
949
static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val)
950
{
951
struct sd *sd = (struct sd *) gspca_dev;
952
953
sd->red_balance = val;
954
if (gspca_dev->streaming)
955
setredbalance(gspca_dev);
956
return gspca_dev->usb_err;
957
}
958
959
static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val)
960
{
961
struct sd *sd = (struct sd *) gspca_dev;
962
963
*val = sd->red_balance;
964
return 0;
965
}
966
967
static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val)
968
{
969
struct sd *sd = (struct sd *) gspca_dev;
970
971
sd->blue_balance = val;
972
if (gspca_dev->streaming)
973
setbluebalance(gspca_dev);
974
return gspca_dev->usb_err;
975
}
976
977
static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val)
978
{
979
struct sd *sd = (struct sd *) gspca_dev;
980
981
*val = sd->blue_balance;
982
return 0;
983
}
984
985
static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
986
{
987
struct sd *sd = (struct sd *) gspca_dev;
988
989
sd->gain = val;
990
if (gspca_dev->streaming)
991
setgain(gspca_dev);
992
return gspca_dev->usb_err;
993
}
994
995
static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
996
{
997
struct sd *sd = (struct sd *) gspca_dev;
998
999
*val = sd->gain;
1000
return 0;
1001
}
1002
1003
static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
1004
{
1005
struct sd *sd = (struct sd *) gspca_dev;
1006
1007
sd->exposure = val;
1008
if (gspca_dev->streaming)
1009
setexposure(gspca_dev);
1010
return gspca_dev->usb_err;
1011
}
1012
1013
static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
1014
{
1015
struct sd *sd = (struct sd *) gspca_dev;
1016
1017
*val = sd->exposure;
1018
return 0;
1019
}
1020
1021
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
1022
{
1023
struct sd *sd = (struct sd *) gspca_dev;
1024
1025
sd->autogain = val;
1026
/* when switching to autogain set defaults to make sure
1027
we are on a valid point of the autogain gain /
1028
exposure knee graph, and give this change time to
1029
take effect before doing autogain. */
1030
if (sd->autogain) {
1031
sd->exposure = EXPOSURE_DEF;
1032
sd->gain = GAIN_DEF;
1033
if (gspca_dev->streaming) {
1034
sd->autogain_ignore_frames =
1035
PAC_AUTOGAIN_IGNORE_FRAMES;
1036
setexposure(gspca_dev);
1037
setgain(gspca_dev);
1038
}
1039
}
1040
1041
return gspca_dev->usb_err;
1042
}
1043
1044
static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
1045
{
1046
struct sd *sd = (struct sd *) gspca_dev;
1047
1048
*val = sd->autogain;
1049
return 0;
1050
}
1051
1052
static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
1053
{
1054
struct sd *sd = (struct sd *) gspca_dev;
1055
1056
sd->hflip = val;
1057
if (gspca_dev->streaming)
1058
sethvflip(gspca_dev);
1059
return gspca_dev->usb_err;
1060
}
1061
1062
static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
1063
{
1064
struct sd *sd = (struct sd *) gspca_dev;
1065
1066
*val = sd->hflip;
1067
return 0;
1068
}
1069
1070
static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
1071
{
1072
struct sd *sd = (struct sd *) gspca_dev;
1073
1074
sd->vflip = val;
1075
if (gspca_dev->streaming)
1076
sethvflip(gspca_dev);
1077
return gspca_dev->usb_err;
1078
}
1079
1080
static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
1081
{
1082
struct sd *sd = (struct sd *) gspca_dev;
1083
1084
*val = sd->vflip;
1085
return 0;
1086
}
1087
1088
#ifdef CONFIG_VIDEO_ADV_DEBUG
1089
static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
1090
struct v4l2_dbg_register *reg)
1091
{
1092
__u8 index;
1093
__u8 value;
1094
1095
/* reg->reg: bit0..15: reserved for register index (wIndex is 16bit
1096
long on the USB bus)
1097
*/
1098
if (reg->match.type == V4L2_CHIP_MATCH_HOST &&
1099
reg->match.addr == 0 &&
1100
(reg->reg < 0x000000ff) &&
1101
(reg->val <= 0x000000ff)
1102
) {
1103
/* Currently writing to page 0 is only supported. */
1104
/* reg_w() only supports 8bit index */
1105
index = reg->reg & 0x000000ff;
1106
value = reg->val & 0x000000ff;
1107
1108
/* Note that there shall be no access to other page
1109
by any other function between the page swith and
1110
the actual register write */
1111
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
1112
reg_w(gspca_dev, index, value);
1113
1114
reg_w(gspca_dev, 0xdc, 0x01);
1115
}
1116
return gspca_dev->usb_err;
1117
}
1118
1119
static int sd_chip_ident(struct gspca_dev *gspca_dev,
1120
struct v4l2_dbg_chip_ident *chip)
1121
{
1122
int ret = -EINVAL;
1123
1124
if (chip->match.type == V4L2_CHIP_MATCH_HOST &&
1125
chip->match.addr == 0) {
1126
chip->revision = 0;
1127
chip->ident = V4L2_IDENT_UNKNOWN;
1128
ret = 0;
1129
}
1130
return ret;
1131
}
1132
#endif
1133
1134
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
1135
static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
1136
u8 *data, /* interrupt packet data */
1137
int len) /* interrput packet length */
1138
{
1139
int ret = -EINVAL;
1140
u8 data0, data1;
1141
1142
if (len == 2) {
1143
data0 = data[0];
1144
data1 = data[1];
1145
if ((data0 == 0x00 && data1 == 0x11) ||
1146
(data0 == 0x22 && data1 == 0x33) ||
1147
(data0 == 0x44 && data1 == 0x55) ||
1148
(data0 == 0x66 && data1 == 0x77) ||
1149
(data0 == 0x88 && data1 == 0x99) ||
1150
(data0 == 0xaa && data1 == 0xbb) ||
1151
(data0 == 0xcc && data1 == 0xdd) ||
1152
(data0 == 0xee && data1 == 0xff)) {
1153
input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
1154
input_sync(gspca_dev->input_dev);
1155
input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
1156
input_sync(gspca_dev->input_dev);
1157
ret = 0;
1158
}
1159
}
1160
1161
return ret;
1162
}
1163
#endif
1164
1165
/* sub-driver description for pac7302 */
1166
static const struct sd_desc sd_desc = {
1167
.name = MODULE_NAME,
1168
.ctrls = sd_ctrls,
1169
.nctrls = ARRAY_SIZE(sd_ctrls),
1170
.config = sd_config,
1171
.init = sd_init,
1172
.start = sd_start,
1173
.stopN = sd_stopN,
1174
.stop0 = sd_stop0,
1175
.pkt_scan = sd_pkt_scan,
1176
.dq_callback = do_autogain,
1177
#ifdef CONFIG_VIDEO_ADV_DEBUG
1178
.set_register = sd_dbg_s_register,
1179
.get_chip_ident = sd_chip_ident,
1180
#endif
1181
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
1182
.int_pkt_scan = sd_int_pkt_scan,
1183
#endif
1184
};
1185
1186
/* -- module initialisation -- */
1187
static const struct usb_device_id device_table[] = {
1188
{USB_DEVICE(0x06f8, 0x3009)},
1189
{USB_DEVICE(0x093a, 0x2620)},
1190
{USB_DEVICE(0x093a, 0x2621)},
1191
{USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP},
1192
{USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP},
1193
{USB_DEVICE(0x093a, 0x2625)},
1194
{USB_DEVICE(0x093a, 0x2626)},
1195
{USB_DEVICE(0x093a, 0x2628)},
1196
{USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP},
1197
{USB_DEVICE(0x093a, 0x262a)},
1198
{USB_DEVICE(0x093a, 0x262c)},
1199
{}
1200
};
1201
MODULE_DEVICE_TABLE(usb, device_table);
1202
1203
/* -- device connect -- */
1204
static int sd_probe(struct usb_interface *intf,
1205
const struct usb_device_id *id)
1206
{
1207
return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
1208
THIS_MODULE);
1209
}
1210
1211
static struct usb_driver sd_driver = {
1212
.name = MODULE_NAME,
1213
.id_table = device_table,
1214
.probe = sd_probe,
1215
.disconnect = gspca_disconnect,
1216
#ifdef CONFIG_PM
1217
.suspend = gspca_suspend,
1218
.resume = gspca_resume,
1219
#endif
1220
};
1221
1222
/* -- module insert / remove -- */
1223
static int __init sd_mod_init(void)
1224
{
1225
return usb_register(&sd_driver);
1226
}
1227
static void __exit sd_mod_exit(void)
1228
{
1229
usb_deregister(&sd_driver);
1230
}
1231
1232
module_init(sd_mod_init);
1233
module_exit(sd_mod_exit);
1234
1235