📄 alauda.c
字号:
/* * MTD driver for Alauda chips * * Copyright (C) 2007 Joern Engel <joern@logfs.org> * * Based on drivers/usb/usb-skeleton.c which is: * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) * and on drivers/usb/storage/alauda.c, which is: * (c) 2005 Daniel Drake <dsd@gentoo.org> * * Idea and initial work by Arnd Bergmann <arnd@arndb.de> */#include <linux/kernel.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/kref.h>#include <linux/usb.h>#include <linux/mutex.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nand_ecc.h>/* Control commands */#define ALAUDA_GET_XD_MEDIA_STATUS 0x08#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a#define ALAUDA_GET_XD_MEDIA_SIG 0x86/* Common prefix */#define ALAUDA_BULK_CMD 0x40/* The two ports */#define ALAUDA_PORT_XD 0x00#define ALAUDA_PORT_SM 0x01/* Bulk commands */#define ALAUDA_BULK_READ_PAGE 0x84#define ALAUDA_BULK_READ_OOB 0x85 /* don't use, there's a chip bug */#define ALAUDA_BULK_READ_BLOCK 0x94#define ALAUDA_BULK_ERASE_BLOCK 0xa3#define ALAUDA_BULK_WRITE_PAGE 0xa4#define ALAUDA_BULK_WRITE_BLOCK 0xb4#define ALAUDA_BULK_RESET_MEDIA 0xe0/* Address shifting */#define PBA_LO(pba) ((pba & 0xF) << 5)#define PBA_HI(pba) (pba >> 3)#define PBA_ZONE(pba) (pba >> 11)#define TIMEOUT HZstatic struct usb_device_id alauda_table [] = { { USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */ { USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */ { }};MODULE_DEVICE_TABLE(usb, alauda_table);struct alauda_card { u8 id; /* id byte */ u8 chipshift; /* 1<<chipshift total size */ u8 pageshift; /* 1<<pageshift page size */ u8 blockshift; /* 1<<blockshift block size */};struct alauda { struct usb_device *dev; struct usb_interface *interface; struct mtd_info *mtd; struct alauda_card *card; struct mutex card_mutex; u32 pagemask; u32 bytemask; u32 blockmask; unsigned int write_out; unsigned int bulk_in; unsigned int bulk_out; u8 port; struct kref kref;};static struct alauda_card alauda_card_ids[] = { /* NAND flash */ { 0x6e, 20, 8, 12}, /* 1 MB */ { 0xe8, 20, 8, 12}, /* 1 MB */ { 0xec, 20, 8, 12}, /* 1 MB */ { 0x64, 21, 8, 12}, /* 2 MB */ { 0xea, 21, 8, 12}, /* 2 MB */ { 0x6b, 22, 9, 13}, /* 4 MB */ { 0xe3, 22, 9, 13}, /* 4 MB */ { 0xe5, 22, 9, 13}, /* 4 MB */ { 0xe6, 23, 9, 13}, /* 8 MB */ { 0x73, 24, 9, 14}, /* 16 MB */ { 0x75, 25, 9, 14}, /* 32 MB */ { 0x76, 26, 9, 14}, /* 64 MB */ { 0x79, 27, 9, 14}, /* 128 MB */ { 0x71, 28, 9, 14}, /* 256 MB */ /* MASK ROM */ { 0x5d, 21, 9, 13}, /* 2 MB */ { 0xd5, 22, 9, 13}, /* 4 MB */ { 0xd6, 23, 9, 13}, /* 8 MB */ { 0x57, 24, 9, 13}, /* 16 MB */ { 0x58, 25, 9, 13}, /* 32 MB */ { }};static struct alauda_card *get_card(u8 id){ struct alauda_card *card; for (card = alauda_card_ids; card->id; card++) if (card->id == id) return card; return NULL;}static void alauda_delete(struct kref *kref){ struct alauda *al = container_of(kref, struct alauda, kref); if (al->mtd) { del_mtd_device(al->mtd); kfree(al->mtd); } usb_put_dev(al->dev); kfree(al);}static int alauda_get_media_status(struct alauda *al, void *buf){ int ret; mutex_lock(&al->card_mutex); ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), ALAUDA_GET_XD_MEDIA_STATUS, 0xc0, 0, 1, buf, 2, HZ); mutex_unlock(&al->card_mutex); return ret;}static int alauda_ack_media(struct alauda *al){ int ret; mutex_lock(&al->card_mutex); ret = usb_control_msg(al->dev, usb_sndctrlpipe(al->dev, 0), ALAUDA_ACK_XD_MEDIA_CHANGE, 0x40, 0, 1, NULL, 0, HZ); mutex_unlock(&al->card_mutex); return ret;}static int alauda_get_media_signatures(struct alauda *al, void *buf){ int ret; mutex_lock(&al->card_mutex); ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), ALAUDA_GET_XD_MEDIA_SIG, 0xc0, 0, 0, buf, 4, HZ); mutex_unlock(&al->card_mutex); return ret;}static void alauda_reset(struct alauda *al){ u8 command[] = { ALAUDA_BULK_CMD, ALAUDA_BULK_RESET_MEDIA, 0, 0, 0, 0, 0, 0, al->port }; mutex_lock(&al->card_mutex); usb_bulk_msg(al->dev, al->bulk_out, command, 9, NULL, HZ); mutex_unlock(&al->card_mutex);}static void correct_data(void *buf, void *read_ecc, int *corrected, int *uncorrected){ u8 calc_ecc[3]; int err; nand_calculate_ecc(NULL, buf, calc_ecc); err = nand_correct_data(NULL, buf, read_ecc, calc_ecc); if (err) { if (err > 0) (*corrected)++; else (*uncorrected)++; }}struct alauda_sg_request { struct urb *urb[3]; struct completion comp;};static void alauda_complete(struct urb *urb){ struct completion *comp = urb->context; if (comp) complete(comp);}static int __alauda_read_page(struct mtd_info *mtd, loff_t from, void *buf, void *oob){ struct alauda_sg_request sg; struct alauda *al = mtd->priv; u32 pba = from >> al->card->blockshift; u32 page = (from >> al->card->pageshift) & al->pagemask; u8 command[] = { ALAUDA_BULK_CMD, ALAUDA_BULK_READ_PAGE, PBA_HI(pba), PBA_ZONE(pba), 0, PBA_LO(pba) + page, 1, 0, al->port }; int i, err; for (i=0; i<3; i++) sg.urb[i] = NULL; err = -ENOMEM; for (i=0; i<3; i++) { sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); if (!sg.urb[i]) goto out; } init_completion(&sg.comp); usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, alauda_complete, NULL); usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, mtd->writesize, alauda_complete, NULL); usb_fill_bulk_urb(sg.urb[2], al->dev, al->bulk_in, oob, 16, alauda_complete, &sg.comp); mutex_lock(&al->card_mutex); for (i=0; i<3; i++) { err = usb_submit_urb(sg.urb[i], GFP_NOIO); if (err) goto cancel; } if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { err = -ETIMEDOUT;cancel: for (i=0; i<3; i++) { usb_kill_urb(sg.urb[i]); } } mutex_unlock(&al->card_mutex);out: usb_free_urb(sg.urb[0]); usb_free_urb(sg.urb[1]); usb_free_urb(sg.urb[2]); return err;}static int alauda_read_page(struct mtd_info *mtd, loff_t from, void *buf, u8 *oob, int *corrected, int *uncorrected){ int err; err = __alauda_read_page(mtd, from, buf, oob); if (err) return err; correct_data(buf, oob+13, corrected, uncorrected); correct_data(buf+256, oob+8, corrected, uncorrected); return 0;}static int alauda_write_page(struct mtd_info *mtd, loff_t to, void *buf, void *oob){ struct alauda_sg_request sg; struct alauda *al = mtd->priv; u32 pba = to >> al->card->blockshift; u32 page = (to >> al->card->pageshift) & al->pagemask; u8 command[] = { ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_PAGE, PBA_HI(pba), PBA_ZONE(pba), 0, PBA_LO(pba) + page, 32, 0, al->port }; int i, err; for (i=0; i<3; i++) sg.urb[i] = NULL; err = -ENOMEM; for (i=0; i<3; i++) { sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); if (!sg.urb[i]) goto out; } init_completion(&sg.comp); usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, alauda_complete, NULL); usb_fill_bulk_urb(sg.urb[1], al->dev, al->write_out, buf,mtd->writesize, alauda_complete, NULL); usb_fill_bulk_urb(sg.urb[2], al->dev, al->write_out, oob, 16, alauda_complete, &sg.comp); mutex_lock(&al->card_mutex); for (i=0; i<3; i++) { err = usb_submit_urb(sg.urb[i], GFP_NOIO); if (err) goto cancel; } if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { err = -ETIMEDOUT;cancel: for (i=0; i<3; i++) { usb_kill_urb(sg.urb[i]); } } mutex_unlock(&al->card_mutex);out: usb_free_urb(sg.urb[0]); usb_free_urb(sg.urb[1]); usb_free_urb(sg.urb[2]); return err;}static int alauda_erase_block(struct mtd_info *mtd, loff_t ofs){ struct alauda_sg_request sg; struct alauda *al = mtd->priv; u32 pba = ofs >> al->card->blockshift; u8 command[] = { ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba), PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, al->port }; u8 buf[2]; int i, err; for (i=0; i<2; i++) sg.urb[i] = NULL; err = -ENOMEM; for (i=0; i<2; i++) { sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); if (!sg.urb[i]) goto out; } init_completion(&sg.comp); usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, alauda_complete, NULL); usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, 2, alauda_complete, &sg.comp); mutex_lock(&al->card_mutex); for (i=0; i<2; i++) { err = usb_submit_urb(sg.urb[i], GFP_NOIO); if (err) goto cancel; } if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { err = -ETIMEDOUT;cancel: for (i=0; i<2; i++) { usb_kill_urb(sg.urb[i]); } } mutex_unlock(&al->card_mutex);out: usb_free_urb(sg.urb[0]); usb_free_urb(sg.urb[1]); return err;}static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob){ static u8 ignore_buf[512]; /* write only */ return __alauda_read_page(mtd, from, ignore_buf, oob);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -