0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include "gspca.h"
0014
0015 #define MODULE_NAME "touptek"
0016
0017 MODULE_AUTHOR("John McMaster");
0018 MODULE_DESCRIPTION("ToupTek UCMOS / Amscope MU microscope camera driver");
0019 MODULE_LICENSE("GPL");
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096 #define GAIN_MAX 511
0097
0098
0099 #define BULK_SIZE 0x4000
0100
0101
0102 #define REG_COARSE_INTEGRATION_TIME_ 0x3012
0103 #define REG_GROUPED_PARAMETER_HOLD_ 0x3022
0104 #define REG_MODE_SELECT 0x0100
0105 #define REG_OP_SYS_CLK_DIV 0x030A
0106 #define REG_VT_SYS_CLK_DIV 0x0302
0107 #define REG_PRE_PLL_CLK_DIV 0x0304
0108 #define REG_VT_PIX_CLK_DIV 0x0300
0109 #define REG_OP_PIX_CLK_DIV 0x0308
0110 #define REG_PLL_MULTIPLIER 0x0306
0111 #define REG_COARSE_INTEGRATION_TIME_ 0x3012
0112 #define REG_FRAME_LENGTH_LINES 0x0340
0113 #define REG_FRAME_LENGTH_LINES_ 0x300A
0114 #define REG_GREEN1_GAIN 0x3056
0115 #define REG_GREEN2_GAIN 0x305C
0116 #define REG_GROUPED_PARAMETER_HOLD 0x0104
0117 #define REG_LINE_LENGTH_PCK_ 0x300C
0118 #define REG_MODE_SELECT 0x0100
0119 #define REG_PLL_MULTIPLIER 0x0306
0120 #define REG_READ_MODE 0x3040
0121 #define REG_BLUE_GAIN 0x3058
0122 #define REG_RED_GAIN 0x305A
0123 #define REG_RESET_REGISTER 0x301A
0124 #define REG_SCALE_M 0x0404
0125 #define REG_SCALING_MODE 0x0400
0126 #define REG_SOFTWARE_RESET 0x0103
0127 #define REG_X_ADDR_END 0x0348
0128 #define REG_X_ADDR_START 0x0344
0129 #define REG_X_ADDR_START 0x0344
0130 #define REG_X_OUTPUT_SIZE 0x034C
0131 #define REG_Y_ADDR_END 0x034A
0132 #define REG_Y_ADDR_START 0x0346
0133 #define REG_Y_OUTPUT_SIZE 0x034E
0134
0135
0136
0137 struct sd {
0138 struct gspca_dev gspca_dev;
0139
0140 unsigned int this_f;
0141
0142
0143
0144
0145
0146
0147 struct v4l2_ctrl *blue;
0148 struct v4l2_ctrl *red;
0149 };
0150
0151
0152 struct cmd {
0153 u16 value;
0154 u16 index;
0155 };
0156
0157 static const struct v4l2_pix_format vga_mode[] = {
0158 {800, 600,
0159 V4L2_PIX_FMT_SGRBG8,
0160 V4L2_FIELD_NONE,
0161 .bytesperline = 800,
0162 .sizeimage = 800 * 600,
0163 .colorspace = V4L2_COLORSPACE_SRGB},
0164 {1600, 1200,
0165 V4L2_PIX_FMT_SGRBG8,
0166 V4L2_FIELD_NONE,
0167 .bytesperline = 1600,
0168 .sizeimage = 1600 * 1200,
0169 .colorspace = V4L2_COLORSPACE_SRGB},
0170 {3264, 2448,
0171 V4L2_PIX_FMT_SGRBG8,
0172 V4L2_FIELD_NONE,
0173 .bytesperline = 3264,
0174 .sizeimage = 3264 * 2448,
0175 .colorspace = V4L2_COLORSPACE_SRGB},
0176 };
0177
0178
0179
0180
0181
0182 #if MAX_NURBS < 4
0183 #error "Not enough URBs in the gspca table"
0184 #endif
0185
0186 static int val_reply(struct gspca_dev *gspca_dev, const char *reply, int rc)
0187 {
0188 if (rc < 0) {
0189 gspca_err(gspca_dev, "reply has error %d\n", rc);
0190 return -EIO;
0191 }
0192 if (rc != 1) {
0193 gspca_err(gspca_dev, "Bad reply size %d\n", rc);
0194 return -EIO;
0195 }
0196 if (reply[0] != 0x08) {
0197 gspca_err(gspca_dev, "Bad reply 0x%02x\n", (int)reply[0]);
0198 return -EIO;
0199 }
0200 return 0;
0201 }
0202
0203 static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index)
0204 {
0205 char *buff = gspca_dev->usb_buf;
0206 int rc;
0207
0208 gspca_dbg(gspca_dev, D_USBO,
0209 "reg_w bReq=0x0B, bReqT=0xC0, wVal=0x%04X, wInd=0x%04X\n\n",
0210 value, index);
0211 rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0),
0212 0x0B, 0xC0, value, index, buff, 1, 500);
0213 gspca_dbg(gspca_dev, D_USBO, "rc=%d, ret={0x%02x}\n", rc, (int)buff[0]);
0214 if (rc < 0) {
0215 gspca_err(gspca_dev, "Failed reg_w(0x0B, 0xC0, 0x%04X, 0x%04X) w/ rc %d\n",
0216 value, index, rc);
0217 gspca_dev->usb_err = rc;
0218 return;
0219 }
0220 if (val_reply(gspca_dev, buff, rc)) {
0221 gspca_err(gspca_dev, "Bad reply to reg_w(0x0B, 0xC0, 0x%04X, 0x%04X\n",
0222 value, index);
0223 gspca_dev->usb_err = -EIO;
0224 }
0225 }
0226
0227 static void reg_w_buf(struct gspca_dev *gspca_dev,
0228 const struct cmd *p, int l)
0229 {
0230 do {
0231 reg_w(gspca_dev, p->value, p->index);
0232 p++;
0233 } while (--l > 0);
0234 }
0235
0236 static void setexposure(struct gspca_dev *gspca_dev, s32 val)
0237 {
0238 u16 value;
0239 unsigned int w = gspca_dev->pixfmt.width;
0240
0241 if (w == 800)
0242 value = val * 5;
0243 else if (w == 1600)
0244 value = val * 3;
0245 else if (w == 3264)
0246 value = val * 3 / 2;
0247 else {
0248 gspca_err(gspca_dev, "Invalid width %u\n", w);
0249 gspca_dev->usb_err = -EINVAL;
0250 return;
0251 }
0252 gspca_dbg(gspca_dev, D_STREAM, "exposure: 0x%04X ms\n\n", value);
0253
0254
0255 reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_);
0256 reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_);
0257 }
0258
0259 static int gainify(int in)
0260 {
0261
0262
0263
0264
0265
0266
0267 if (in <= 0x7F)
0268 return 0x1000 | in;
0269 else if (in <= 0xFF)
0270 return 0x1080 | in / 2;
0271 else
0272 return 0x1180 | in / 4;
0273 }
0274
0275 static void setggain(struct gspca_dev *gspca_dev, u16 global_gain)
0276 {
0277 u16 normalized;
0278
0279 normalized = gainify(global_gain);
0280 gspca_dbg(gspca_dev, D_STREAM, "gain G1/G2 (0x%04X): 0x%04X (src 0x%04X)\n\n",
0281 REG_GREEN1_GAIN,
0282 normalized, global_gain);
0283
0284 reg_w(gspca_dev, normalized, REG_GREEN1_GAIN);
0285 reg_w(gspca_dev, normalized, REG_GREEN2_GAIN);
0286 }
0287
0288 static void setbgain(struct gspca_dev *gspca_dev,
0289 u16 gain, u16 global_gain)
0290 {
0291 u16 normalized;
0292
0293 normalized = global_gain +
0294 ((u32)global_gain) * gain / GAIN_MAX;
0295 if (normalized > GAIN_MAX) {
0296 gspca_dbg(gspca_dev, D_STREAM, "Truncating blue 0x%04X w/ value 0x%04X\n\n",
0297 GAIN_MAX, normalized);
0298 normalized = GAIN_MAX;
0299 }
0300 normalized = gainify(normalized);
0301 gspca_dbg(gspca_dev, D_STREAM, "gain B (0x%04X): 0x%04X w/ source 0x%04X\n\n",
0302 REG_BLUE_GAIN, normalized, gain);
0303
0304 reg_w(gspca_dev, normalized, REG_BLUE_GAIN);
0305 }
0306
0307 static void setrgain(struct gspca_dev *gspca_dev,
0308 u16 gain, u16 global_gain)
0309 {
0310 u16 normalized;
0311
0312 normalized = global_gain +
0313 ((u32)global_gain) * gain / GAIN_MAX;
0314 if (normalized > GAIN_MAX) {
0315 gspca_dbg(gspca_dev, D_STREAM, "Truncating gain 0x%04X w/ value 0x%04X\n\n",
0316 GAIN_MAX, normalized);
0317 normalized = GAIN_MAX;
0318 }
0319 normalized = gainify(normalized);
0320 gspca_dbg(gspca_dev, D_STREAM, "gain R (0x%04X): 0x%04X w / source 0x%04X\n\n",
0321 REG_RED_GAIN, normalized, gain);
0322
0323 reg_w(gspca_dev, normalized, REG_RED_GAIN);
0324 }
0325
0326 static void configure_wh(struct gspca_dev *gspca_dev)
0327 {
0328 unsigned int w = gspca_dev->pixfmt.width;
0329
0330 gspca_dbg(gspca_dev, D_STREAM, "configure_wh\n\n");
0331
0332 if (w == 800) {
0333 static const struct cmd reg_init_res[] = {
0334 {0x0060, REG_X_ADDR_START},
0335 {0x0CD9, REG_X_ADDR_END},
0336 {0x0036, REG_Y_ADDR_START},
0337 {0x098F, REG_Y_ADDR_END},
0338 {0x07C7, REG_READ_MODE},
0339 };
0340
0341 reg_w_buf(gspca_dev,
0342 reg_init_res, ARRAY_SIZE(reg_init_res));
0343 } else if (w == 1600) {
0344 static const struct cmd reg_init_res[] = {
0345 {0x009C, REG_X_ADDR_START},
0346 {0x0D19, REG_X_ADDR_END},
0347 {0x0068, REG_Y_ADDR_START},
0348 {0x09C5, REG_Y_ADDR_END},
0349 {0x06C3, REG_READ_MODE},
0350 };
0351
0352 reg_w_buf(gspca_dev,
0353 reg_init_res, ARRAY_SIZE(reg_init_res));
0354 } else if (w == 3264) {
0355 static const struct cmd reg_init_res[] = {
0356 {0x00E8, REG_X_ADDR_START},
0357 {0x0DA7, REG_X_ADDR_END},
0358 {0x009E, REG_Y_ADDR_START},
0359 {0x0A2D, REG_Y_ADDR_END},
0360 {0x0241, REG_READ_MODE},
0361 };
0362
0363 reg_w_buf(gspca_dev,
0364 reg_init_res, ARRAY_SIZE(reg_init_res));
0365 } else {
0366 gspca_err(gspca_dev, "bad width %u\n", w);
0367 gspca_dev->usb_err = -EINVAL;
0368 return;
0369 }
0370
0371 reg_w(gspca_dev, 0x0000, REG_SCALING_MODE);
0372 reg_w(gspca_dev, 0x0010, REG_SCALE_M);
0373 reg_w(gspca_dev, w, REG_X_OUTPUT_SIZE);
0374 reg_w(gspca_dev, gspca_dev->pixfmt.height, REG_Y_OUTPUT_SIZE);
0375
0376 if (w == 800) {
0377 reg_w(gspca_dev, 0x0384, REG_FRAME_LENGTH_LINES_);
0378 reg_w(gspca_dev, 0x0960, REG_LINE_LENGTH_PCK_);
0379 } else if (w == 1600) {
0380 reg_w(gspca_dev, 0x0640, REG_FRAME_LENGTH_LINES_);
0381 reg_w(gspca_dev, 0x0FA0, REG_LINE_LENGTH_PCK_);
0382 } else if (w == 3264) {
0383 reg_w(gspca_dev, 0x0B4B, REG_FRAME_LENGTH_LINES_);
0384 reg_w(gspca_dev, 0x1F40, REG_LINE_LENGTH_PCK_);
0385 } else {
0386 gspca_err(gspca_dev, "bad width %u\n", w);
0387 gspca_dev->usb_err = -EINVAL;
0388 return;
0389 }
0390 }
0391
0392
0393 static void configure_encrypted(struct gspca_dev *gspca_dev)
0394 {
0395 static const struct cmd reg_init_begin[] = {
0396 {0x0100, REG_SOFTWARE_RESET},
0397 {0x0000, REG_MODE_SELECT},
0398 {0x0100, REG_GROUPED_PARAMETER_HOLD},
0399 {0x0004, REG_VT_PIX_CLK_DIV},
0400 {0x0001, REG_VT_SYS_CLK_DIV},
0401 {0x0008, REG_OP_PIX_CLK_DIV},
0402 {0x0001, REG_OP_SYS_CLK_DIV},
0403 {0x0004, REG_PRE_PLL_CLK_DIV},
0404 {0x0040, REG_PLL_MULTIPLIER},
0405 {0x0000, REG_GROUPED_PARAMETER_HOLD},
0406 {0x0100, REG_GROUPED_PARAMETER_HOLD},
0407 };
0408 static const struct cmd reg_init_end[] = {
0409 {0x0000, REG_GROUPED_PARAMETER_HOLD},
0410 {0x0301, 0x31AE},
0411 {0x0805, 0x3064},
0412 {0x0071, 0x3170},
0413 {0x10DE, REG_RESET_REGISTER},
0414 {0x0000, REG_MODE_SELECT},
0415 {0x0010, REG_PLL_MULTIPLIER},
0416 {0x0100, REG_MODE_SELECT},
0417 };
0418
0419 gspca_dbg(gspca_dev, D_STREAM, "Encrypted begin, w = %u\n\n",
0420 gspca_dev->pixfmt.width);
0421 reg_w_buf(gspca_dev, reg_init_begin, ARRAY_SIZE(reg_init_begin));
0422 configure_wh(gspca_dev);
0423 reg_w_buf(gspca_dev, reg_init_end, ARRAY_SIZE(reg_init_end));
0424 reg_w(gspca_dev, 0x0100, REG_GROUPED_PARAMETER_HOLD);
0425 reg_w(gspca_dev, 0x0000, REG_GROUPED_PARAMETER_HOLD);
0426
0427 gspca_dbg(gspca_dev, D_STREAM, "Encrypted end\n\n");
0428 }
0429
0430 static int configure(struct gspca_dev *gspca_dev)
0431 {
0432 int rc;
0433 char *buff = gspca_dev->usb_buf;
0434
0435 gspca_dbg(gspca_dev, D_STREAM, "configure()\n\n");
0436
0437
0438
0439
0440
0441
0442
0443
0444
0445
0446
0447
0448
0449
0450 rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0),
0451 0x16, 0xC0, 0x0000, 0x0000, buff, 2, 500);
0452 if (val_reply(gspca_dev, buff, rc)) {
0453 gspca_err(gspca_dev, "failed key req\n");
0454 return -EIO;
0455 }
0456
0457
0458
0459
0460
0461
0462
0463
0464
0465
0466
0467 rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
0468 0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500);
0469 if (rc < 0) {
0470 gspca_err(gspca_dev, "failed to replay packet 176 w/ rc %d\n",
0471 rc);
0472 return rc;
0473 }
0474
0475 rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
0476 0x01, 0x40, 0x0000, 0x000F, NULL, 0, 500);
0477 if (rc < 0) {
0478 gspca_err(gspca_dev, "failed to replay packet 178 w/ rc %d\n",
0479 rc);
0480 return rc;
0481 }
0482
0483 rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
0484 0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500);
0485 if (rc < 0) {
0486 gspca_err(gspca_dev, "failed to replay packet 180 w/ rc %d\n",
0487 rc);
0488 return rc;
0489 }
0490
0491
0492
0493
0494
0495
0496
0497
0498
0499
0500 gspca_dev->usb_err = 0;
0501 configure_encrypted(gspca_dev);
0502 if (gspca_dev->usb_err)
0503 return gspca_dev->usb_err;
0504
0505
0506 rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0),
0507 0x01, 0x40, 0x0003, 0x000F, NULL, 0, 500);
0508 if (rc < 0) {
0509 gspca_err(gspca_dev, "failed to replay final packet w/ rc %d\n",
0510 rc);
0511 return rc;
0512 }
0513
0514 gspca_dbg(gspca_dev, D_STREAM, "Configure complete\n\n");
0515 return 0;
0516 }
0517
0518 static int sd_config(struct gspca_dev *gspca_dev,
0519 const struct usb_device_id *id)
0520 {
0521 gspca_dev->cam.cam_mode = vga_mode;
0522 gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
0523
0524
0525 gspca_dev->cam.no_urb_create = 0;
0526 gspca_dev->cam.bulk_nurbs = 4;
0527
0528 gspca_dev->cam.bulk_size = BULK_SIZE;
0529
0530 gspca_dev->cam.bulk = 1;
0531
0532 return 0;
0533 }
0534
0535 static int sd_start(struct gspca_dev *gspca_dev)
0536 {
0537 struct sd *sd = (struct sd *) gspca_dev;
0538 int rc;
0539
0540 sd->this_f = 0;
0541
0542 rc = configure(gspca_dev);
0543 if (rc < 0) {
0544 gspca_err(gspca_dev, "Failed configure\n");
0545 return rc;
0546 }
0547
0548
0549 return 0;
0550 }
0551
0552 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
0553 u8 *data,
0554 int len)
0555 {
0556 struct sd *sd = (struct sd *) gspca_dev;
0557
0558 if (len != BULK_SIZE) {
0559
0560 if (sd->this_f + len == gspca_dev->pixfmt.sizeimage) {
0561 gspca_frame_add(gspca_dev, LAST_PACKET, data, len);
0562 gspca_dbg(gspca_dev, D_FRAM, "finish frame sz %u/%u w/ len %u\n\n",
0563 sd->this_f, gspca_dev->pixfmt.sizeimage, len);
0564
0565 } else {
0566 gspca_frame_add(gspca_dev, DISCARD_PACKET, NULL, 0);
0567 gspca_dbg(gspca_dev, D_FRAM, "abort frame sz %u/%u w/ len %u\n\n",
0568 sd->this_f, gspca_dev->pixfmt.sizeimage, len);
0569 }
0570 sd->this_f = 0;
0571 } else {
0572 if (sd->this_f == 0)
0573 gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
0574 else
0575 gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
0576 sd->this_f += len;
0577 }
0578 }
0579
0580 static int sd_init(struct gspca_dev *gspca_dev)
0581 {
0582 return 0;
0583 }
0584
0585 static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
0586 {
0587 struct gspca_dev *gspca_dev =
0588 container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
0589 struct sd *sd = (struct sd *) gspca_dev;
0590
0591 gspca_dev->usb_err = 0;
0592
0593 if (!gspca_dev->streaming)
0594 return 0;
0595
0596 switch (ctrl->id) {
0597 case V4L2_CID_EXPOSURE:
0598 setexposure(gspca_dev, ctrl->val);
0599 break;
0600 case V4L2_CID_GAIN:
0601
0602 setggain(gspca_dev, gspca_dev->gain->val);
0603 break;
0604 case V4L2_CID_BLUE_BALANCE:
0605 sd->blue->val = ctrl->val;
0606 setbgain(gspca_dev, sd->blue->val, gspca_dev->gain->val);
0607 break;
0608 case V4L2_CID_RED_BALANCE:
0609 sd->red->val = ctrl->val;
0610 setrgain(gspca_dev, sd->red->val, gspca_dev->gain->val);
0611 break;
0612 }
0613 return gspca_dev->usb_err;
0614 }
0615
0616 static const struct v4l2_ctrl_ops sd_ctrl_ops = {
0617 .s_ctrl = sd_s_ctrl,
0618 };
0619
0620 static int sd_init_controls(struct gspca_dev *gspca_dev)
0621 {
0622 struct sd *sd = (struct sd *) gspca_dev;
0623 struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
0624
0625 gspca_dev->vdev.ctrl_handler = hdl;
0626 v4l2_ctrl_handler_init(hdl, 4);
0627
0628 gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
0629
0630
0631 V4L2_CID_EXPOSURE, 0, 800, 1, 350);
0632 gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
0633 V4L2_CID_GAIN, 0, 511, 1, 128);
0634 sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
0635 V4L2_CID_BLUE_BALANCE, 0, 1023, 1, 80);
0636 sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
0637 V4L2_CID_RED_BALANCE, 0, 1023, 1, 295);
0638
0639 if (hdl->error) {
0640 gspca_err(gspca_dev, "Could not initialize controls\n");
0641 return hdl->error;
0642 }
0643 return 0;
0644 }
0645
0646
0647 static const struct sd_desc sd_desc = {
0648 .name = MODULE_NAME,
0649 .config = sd_config,
0650 .init = sd_init,
0651 .init_controls = sd_init_controls,
0652 .start = sd_start,
0653 .pkt_scan = sd_pkt_scan,
0654 };
0655
0656
0657 static const struct usb_device_id device_table[] = {
0658
0659
0660
0661
0662
0663
0664
0665
0666
0667 { USB_DEVICE(0x0547, 0x6801) },
0668
0669
0670
0671
0672
0673
0674
0675
0676
0677
0678
0679
0680
0681
0682
0683
0684
0685
0686
0687
0688
0689
0690 { }
0691 };
0692 MODULE_DEVICE_TABLE(usb, device_table);
0693
0694 static int sd_probe(struct usb_interface *intf,
0695 const struct usb_device_id *id)
0696 {
0697 return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
0698 THIS_MODULE);
0699 }
0700
0701 static struct usb_driver sd_driver = {
0702 .name = MODULE_NAME,
0703 .id_table = device_table,
0704 .probe = sd_probe,
0705 .disconnect = gspca_disconnect,
0706 #ifdef CONFIG_PM
0707 .suspend = gspca_suspend,
0708 .resume = gspca_resume,
0709 #endif
0710 };
0711
0712 static int __init sd_mod_init(void)
0713 {
0714 int ret;
0715
0716 ret = usb_register(&sd_driver);
0717 if (ret < 0)
0718 return ret;
0719 return 0;
0720 }
0721 static void __exit sd_mod_exit(void)
0722 {
0723 usb_deregister(&sd_driver);
0724 }
0725
0726 module_init(sd_mod_init);
0727 module_exit(sd_mod_exit);